Generated compiler coverage
One readable page executes every canonical statement and expression family through AST and generated modes.
expressionsloops and loop metadatainclude scopedefine and block scope
Inputs
assign
{
"flag": true,
"dangerous": "&<>\"'",
"empty_list": [],
"empty_map": {},
"page": {
"title": "Guide"
},
"numbers": [
10,
20
],
"lookup": {
"x": "X"
},
"rows": [
{
"name": "A"
},
{
"name": "B"
}
]
}define
{
"layout": "layout.tpl",
"content": {
"template": "card.tpl",
"data": {
"label": "define"
}
}
}templates · parser ranges
--- card.tpl <p class="card">{= label}</p> --- layout.tpl {: values = [0, ...numbers]} {: merged = [...lookup, 'z' => 'Z']} <section> <h1>{= page.title}</h1> <p class="escaped">{= dangerous}</p> <p class="logical">{= flag && 'x'}|{= false || 2}</p> <p class="empty-truthiness">{= empty_list && flag}|{= empty_map && flag}</p> <p>{= values[1]}|{= merged['z']}</p> {? flag && page.title == 'Guide'}<strong>matched</strong>{:}<strong>missed</strong>{/} <p>{= flag ? 'yes' : 'no'}|{= -1 + 3}|{= '' | default('fallback')}</p> <ul> {@ row = rows} <li>{= row.index_}/{= row.size_}:{= row.name}:{= row.first_}:{= row.last_}</li> {:} <li>empty</li> {/} </ul> {+ partial.tpl} {?# content}<p>defined</p>{:}<p>missing</p>{/} {# content label:page.title} </section> --- partial.tpl <p class="included">{= values[2]}</p>
compiled artifacts
ast/manifest.json
{
"schema": 3,
"mode": "ast",
"target": "canonical",
"entry": "layout.tpl",
"delimiters": null,
"sourceDigest": "936a20a5f3b77bc8a454a40c57b55de1118203b69a39aedda5acb0455f495625",
"typeDigest": "30c0b093d1694670ec7117cc02b36e0031780485e6218be94afbdf1be6524db7",
"contractDigest": "b3a300448cfa0263a9e4fb6888adb359317b6f8e0e9e7f40edc29e8c8671bf2d",
"compilerDigest": "fb8049870573426dd093e2af0cf544e3592477ef1af415453fafcf71f0389901",
"files": {
"card.tpl": {
"path": "card.tpl.ast.json",
"sourceDigest": "d299dda7918a928780ca472de4e9ae7a7e4f75367f9086362e90b17f358318bc",
"artifactDigest": "b7d21b8c69cb2d1792c64260271e4822970a4a5c816966d3a0c8d5d074851579",
"lines": [
0,
30
]
},
"layout.tpl": {
"path": "layout.tpl.ast.json",
"sourceDigest": "1ba56a9e17866570e7f4c48301edecc53a4151e0659a2bf2820db0c014c1836b",
"artifactDigest": "2405af019355de6872b618c0264321380cab02c6fc5e80224c5ed80c15fd4b83",
"lines": [
0,
29,
66,
76,
100,
137,
191,
268,
305,
392,
463,
468,
483,
563,
567,
582,
586,
592,
608,
655,
684,
695
]
},
"partial.tpl": {
"path": "partial.tpl.ast.json",
"sourceDigest": "12756a90a2a53d5fe389cce876f6ce983af348dc3353acfe1ec92174feec491b",
"artifactDigest": "5a4d52cc6b5d74cc6afc476f64a71acb907c53262895e145b325ee35799c3743",
"lines": [
0,
38
]
}
}
}
ast/card.tpl.ast.json · card.tpl
{
"type": "Template",
"name": "card.tpl",
"body": [
{
"type": "Text",
"value": "<p class=\"card\">",
"span": [
0,
16
]
},
{
"type": "Echo",
"expr": {
"type": "Var",
"name": "label",
"span": [
19,
24
]
},
"span": [
16,
25
]
},
{
"type": "Text",
"value": "</p>\n",
"span": [
25,
30
]
}
],
"comments": []
}
ast/layout.tpl.ast.json · layout.tpl
{
"type": "Template",
"name": "layout.tpl",
"body": [
{
"type": "Set",
"name": "values",
"expr": {
"type": "List",
"items": [
{
"type": "Literal",
"kind": "number",
"value": 0,
"span": [
13,
14
]
},
{
"type": "Spread",
"expr": {
"type": "Var",
"name": "numbers",
"span": [
19,
26
]
},
"span": [
16,
26
]
}
],
"span": [
12,
27
]
},
"span": [
0,
28
]
},
{
"type": "Set",
"name": "merged",
"expr": {
"type": "Map",
"entries": [
{
"type": "Spread",
"expr": {
"type": "Var",
"name": "lookup",
"span": [
45,
51
]
},
"span": [
42,
51
]
},
{
"key": {
"type": "Literal",
"kind": "string",
"value": "z",
"span": [
53,
56
]
},
"value": {
"type": "Literal",
"kind": "string",
"value": "Z",
"span": [
60,
63
]
}
}
],
"span": [
41,
64
]
},
"span": [
29,
65
]
},
{
"type": "Text",
"value": "<section>\n<h1>",
"span": [
66,
80
]
},
{
"type": "Echo",
"expr": {
"type": "Member",
"object": {
"type": "Var",
"name": "page",
"span": [
83,
87
]
},
"key": "title",
"span": [
83,
93
]
},
"span": [
80,
94
]
},
{
"type": "Text",
"value": "</h1>\n<p class=\"escaped\">",
"span": [
94,
119
]
},
{
"type": "Echo",
"expr": {
"type": "Var",
"name": "dangerous",
"span": [
122,
131
]
},
"span": [
119,
132
]
},
{
"type": "Text",
"value": "</p>\n<p class=\"logical\">",
"span": [
132,
156
]
},
{
"type": "Echo",
"expr": {
"type": "Binary",
"op": "&&",
"left": {
"type": "Var",
"name": "flag",
"span": [
159,
163
]
},
"right": {
"type": "Literal",
"kind": "string",
"value": "x",
"span": [
167,
170
]
},
"span": [
159,
170
]
},
"span": [
156,
171
]
},
{
"type": "Text",
"value": "|",
"span": [
171,
172
]
},
{
"type": "Echo",
"expr": {
"type": "Binary",
"op": "||",
"left": {
"type": "Literal",
"kind": "bool",
"value": false,
"span": [
175,
180
]
},
"right": {
"type": "Literal",
"kind": "number",
"value": 2,
"span": [
184,
185
]
},
"span": [
175,
185
]
},
"span": [
172,
186
]
},
{
"type": "Text",
"value": "</p>\n<p class=\"empty-truthiness\">",
"span": [
186,
219
]
},
{
"type": "Echo",
"expr": {
"type": "Binary",
"op": "&&",
"left": {
"type": "Var",
"name": "empty_list",
"span": [
222,
232
]
},
"right": {
"type": "Var",
"name": "flag",
"span": [
236,
240
]
},
"span": [
222,
240
]
},
"span": [
219,
241
]
},
{
"type": "Text",
"value": "|",
"span": [
241,
242
]
},
{
"type": "Echo",
"expr": {
"type": "Binary",
"op": "&&",
"left": {
"type": "Var",
"name": "empty_map",
"span": [
245,
254
]
},
"right": {
"type": "Var",
"name": "flag",
"span": [
258,
262
]
},
"span": [
245,
262
]
},
"span": [
242,
263
]
},
{
"type": "Text",
"value": "</p>\n<p>",
"span": [
263,
271
]
},
{
"type": "Echo",
"expr": {
"type": "Index",
"object": {
"type": "Var",
"name": "values",
"span": [
274,
280
]
},
"index": {
"type": "Literal",
"kind": "number",
"value": 1,
"span": [
281,
282
]
},
"span": [
274,
283
]
},
"span": [
271,
284
]
},
{
"type": "Text",
"value": "|",
"span": [
284,
285
]
},
{
"type": "Echo",
"expr": {
"type": "Index",
"object": {
"type": "Var",
"name": "merged",
"span": [
288,
294
]
},
"index": {
"type": "Literal",
"kind": "string",
"value": "z",
"span": [
295,
298
]
},
"span": [
288,
299
]
},
"span": [
285,
300
]
},
{
"type": "Text",
"value": "</p>\n",
"span": [
300,
305
]
},
{
"type": "If",
"branches": [
{
"test": {
"type": "Binary",
"op": "&&",
"left": {
"type": "Var",
"name": "flag",
"span": [
308,
312
]
},
"right": {
"type": "Binary",
"op": "==",
"left": {
"type": "Member",
"object": {
"type": "Var",
"name": "page",
"span": [
316,
320
]
},
"key": "title",
"span": [
316,
326
]
},
"right": {
"type": "Literal",
"kind": "string",
"value": "Guide",
"span": [
330,
337
]
},
"span": [
316,
337
]
},
"span": [
308,
337
]
},
"body": [
{
"type": "Text",
"value": "<strong>matched</strong>",
"span": [
338,
362
]
}
],
"span": [
305,
338
]
}
],
"else": [
{
"type": "Text",
"value": "<strong>missed</strong>",
"span": [
365,
388
]
}
],
"span": [
305,
391
]
},
{
"type": "Text",
"value": "\n<p>",
"span": [
391,
395
]
},
{
"type": "Echo",
"expr": {
"type": "Ternary",
"test": {
"type": "Var",
"name": "flag",
"span": [
398,
402
]
},
"then": {
"type": "Literal",
"kind": "string",
"value": "yes",
"span": [
405,
410
]
},
"else": {
"type": "Literal",
"kind": "string",
"value": "no",
"span": [
413,
417
]
},
"span": [
398,
417
]
},
"span": [
395,
418
]
},
{
"type": "Text",
"value": "|",
"span": [
418,
419
]
},
{
"type": "Echo",
"expr": {
"type": "Binary",
"op": "+",
"left": {
"type": "Unary",
"op": "-",
"operand": {
"type": "Literal",
"kind": "number",
"value": 1,
"span": [
423,
424
]
},
"span": [
422,
424
]
},
"right": {
"type": "Literal",
"kind": "number",
"value": 3,
"span": [
427,
428
]
},
"span": [
422,
428
]
},
"span": [
419,
429
]
},
{
"type": "Text",
"value": "|",
"span": [
429,
430
]
},
{
"type": "Echo",
"expr": {
"type": "Call",
"name": "default",
"args": [
{
"type": "Literal",
"kind": "string",
"value": "",
"span": [
433,
435
]
},
{
"type": "Literal",
"kind": "string",
"value": "fallback",
"span": [
446,
456
]
}
],
"span": [
433,
457
]
},
"span": [
430,
458
]
},
{
"type": "Text",
"value": "</p>\n<ul>\n",
"span": [
458,
468
]
},
{
"type": "For",
"name": "row",
"iter": {
"type": "Var",
"name": "rows",
"span": [
477,
481
]
},
"body": [
{
"type": "Text",
"value": "<li>",
"span": [
483,
487
]
},
{
"type": "Echo",
"expr": {
"type": "LoopMeta",
"loop": "row",
"field": "index_",
"span": [
490,
500
]
},
"span": [
487,
501
]
},
{
"type": "Text",
"value": "/",
"span": [
501,
502
]
},
{
"type": "Echo",
"expr": {
"type": "LoopMeta",
"loop": "row",
"field": "size_",
"span": [
505,
514
]
},
"span": [
502,
515
]
},
{
"type": "Text",
"value": ":",
"span": [
515,
516
]
},
{
"type": "Echo",
"expr": {
"type": "Member",
"object": {
"type": "Var",
"name": "row",
"span": [
519,
522
]
},
"key": "name",
"span": [
519,
527
]
},
"span": [
516,
528
]
},
{
"type": "Text",
"value": ":",
"span": [
528,
529
]
},
{
"type": "Echo",
"expr": {
"type": "LoopMeta",
"loop": "row",
"field": "first_",
"span": [
532,
542
]
},
"span": [
529,
543
]
},
{
"type": "Text",
"value": ":",
"span": [
543,
544
]
},
{
"type": "Echo",
"expr": {
"type": "LoopMeta",
"loop": "row",
"field": "last_",
"span": [
547,
556
]
},
"span": [
544,
557
]
},
{
"type": "Text",
"value": "</li>\n",
"span": [
557,
563
]
}
],
"empty": [
{
"type": "Text",
"value": "<li>empty</li>\n",
"span": [
567,
582
]
}
],
"span": [
468,
585
]
},
{
"type": "Text",
"value": "</ul>\n",
"span": [
586,
592
]
},
{
"type": "Include",
"path": "partial.tpl",
"span": [
592,
607
]
},
{
"type": "IfBlock",
"id": "content",
"body": [
{
"type": "Text",
"value": "<p>defined</p>",
"span": [
620,
634
]
}
],
"else": [
{
"type": "Text",
"value": "<p>missing</p>",
"span": [
637,
651
]
}
],
"span": [
608,
654
]
},
{
"type": "Text",
"value": "\n",
"span": [
654,
655
]
},
{
"type": "Block",
"id": "content",
"path": null,
"scope": [
{
"name": "label",
"expr": {
"type": "Member",
"object": {
"type": "Var",
"name": "page",
"span": [
672,
676
]
},
"key": "title",
"span": [
672,
682
]
}
}
],
"span": [
655,
683
]
},
{
"type": "Text",
"value": "</section>\n",
"span": [
684,
695
]
}
],
"comments": []
}
ast/partial.tpl.ast.json · partial.tpl
{
"type": "Template",
"name": "partial.tpl",
"body": [
{
"type": "Text",
"value": "<p class=\"included\">",
"span": [
0,
20
]
},
{
"type": "Echo",
"expr": {
"type": "Index",
"object": {
"type": "Var",
"name": "values",
"span": [
23,
29
]
},
"index": {
"type": "Literal",
"kind": "number",
"value": 2,
"span": [
30,
31
]
},
"span": [
23,
32
]
},
"span": [
20,
33
]
},
{
"type": "Text",
"value": "</p>\n",
"span": [
33,
38
]
}
],
"comments": []
}
generated renderers
typescript · product generated program
{
"schema": 3,
"mode": "gen",
"target": "ts",
"entry": "layout.tpl",
"sourceDigest": "936a20a5f3b77bc8a454a40c57b55de1118203b69a39aedda5acb0455f495625",
"typeDigest": "30c0b093d1694670ec7117cc02b36e0031780485e6218be94afbdf1be6524db7",
"contractDigest": "b3a300448cfa0263a9e4fb6888adb359317b6f8e0e9e7f40edc29e8c8671bf2d",
"compilerDigest": "a81f5f0f977b099b35dd1267160848c835a8aaf7c6865ac99d066a351070cbb0",
"files": {
"compiler-coverage.ts": {
"path": "compiler-coverage.ts",
"artifactDigest": "da324a1e34bb870912a374e84d431ff54ca44354eed16a85e02e4dc0632dcbac"
}
}
}
// Generated.
import { BindError, Frame, RenderContext, RuntimeBindings, RuntimeEnvironment, Scope, TemplateError, bindData, bindMap, checkText, internalBoundary, type MapValue, type PreparedRender, type Program, type RenderOptions, type Span, type Template, type Value } from '@polyspec/template';
export interface Page {
title: string;
}
export interface Row {
name: string;
}
export interface Slot {
template: string | null;
html: string | null;
}
export interface Assign {
flag: boolean;
dangerous: string;
empty_list: Array<string>;
empty_map: Map<string, string>;
page: Page;
numbers: Array<number>;
lookup: Map<string, string>;
rows: Array<Row>;
}
export interface Input_card_tpl { label: string; }
export interface Input_layout_tpl { }
export interface Input_partial_tpl { values: Array<number>; }
export type DefinitionData<T> = Partial<T>;
export interface Definition<T> { template?: string; html?: string; data?: DefinitionData<T>; }
export interface Definitions { content?: Definition<Input_card_tpl>; layout?: Definition<Input_layout_tpl>; }
export interface ArtifactManifest { schema: number; mode: 'gen'; target: 'ts'; entry: string; sourceDigest: string; typeDigest: string; contractDigest: string; files: Record<string, string>; }
type GeneratedDecoder<T> = (value: Value | undefined, path: string) => T;
function generatedTypeError(value: Value | undefined, path: string, expected: string): Error { return new Error(value === null || value === undefined ? path + ' is required' : path + ' is not ' + expected); }
function generatedChecked<T>(decode: GeneratedDecoder<T>, value: Value, path: string, runtime: RuntimeBindings, frame: Frame, span: Span): T { try { return decode(value, path); } catch (error) { if (error instanceof Error) throw runtime.error(frame, span, 'E_RUNTIME_TYPE', error.message); throw error; } }
function generatedDataObject(value: Value, path: string): MapValue { if (value instanceof Map) return value; throw new Error(path + ' is not an object'); }
function generatedPlain(value: unknown): value is object { if (typeof value !== 'object' || value === null) return false; const prototype = Reflect.getPrototypeOf(value); return prototype === Object.prototype || prototype === null; }
function generatedObject(value: Value | undefined, path: string): MapValue { if (value instanceof Map) return value; throw generatedTypeError(value, path, 'an object'); }
function generatedString(value: Value | undefined, path: string): string { if (typeof value === 'string') return value; throw generatedTypeError(value, path, 'a string'); }
function generatedNumber(value: Value | undefined, path: string): number { if (typeof value === 'number') return value; throw generatedTypeError(value, path, 'a number'); }
function generatedBoolean(value: Value | undefined, path: string): boolean { if (typeof value === 'boolean') return value; throw generatedTypeError(value, path, 'a boolean'); }
function generatedOptional<T>(decode: GeneratedDecoder<T>): GeneratedDecoder<T | null> { return (value, path) => value === null || value === undefined ? null : decode(value, path); }
function generatedListOf<T>(item: GeneratedDecoder<T>): GeneratedDecoder<T[]> { return (value, path) => { if (Array.isArray(value)) return value.map((element, index) => item(element, path + '[' + index + ']')); throw generatedTypeError(value, path, 'a list'); }; }
function generatedMapOf<T>(item: GeneratedDecoder<T>): GeneratedDecoder<Map<string, T>> { return (value, path) => new Map([...generatedObject(value, path)].map(([key, element]): [string, T] => [key, item(element, path + '.' + key)])); }
function generatedEntries<T>(value: T[] | Map<string, T> | null): [Value, T][] { if (value === null) return []; if (Array.isArray(value)) return value.map((item, index): [Value, T] => [index, item]); return [...value]; }
function generatedDecode_Page(value: Value | undefined, path: string): Page { const object = generatedObject(value, path); return { title: generatedString(object.get("title"), path + ".title") }; }
function generatedDecode_Row(value: Value | undefined, path: string): Row { const object = generatedObject(value, path); return { name: generatedString(object.get("name"), path + ".name") }; }
function generatedEncode_Row(value: Row): Value { return new Map<string, Value>([["name", value.name]]); }
function generatedDecodeAssign(value: Value | undefined, path: string): Assign { const object = generatedObject(value, path); return { flag: generatedBoolean(object.get("flag"), path + ".flag"), dangerous: generatedString(object.get("dangerous"), path + ".dangerous"), empty_list: generatedListOf(generatedString)(object.get("empty_list"), path + ".empty_list"), empty_map: generatedMapOf(generatedString)(object.get("empty_map"), path + ".empty_map"), page: generatedDecode_Page(object.get("page"), path + ".page"), numbers: generatedListOf(generatedNumber)(object.get("numbers"), path + ".numbers"), lookup: generatedMapOf(generatedString)(object.get("lookup"), path + ".lookup"), rows: generatedListOf(generatedDecode_Row)(object.get("rows"), path + ".rows") }; }
function generatedDecodeData_Input_card_tpl(value: Value, path: string): DefinitionData<Input_card_tpl> { const object = generatedDataObject(value, path); const data: DefinitionData<Input_card_tpl> = {}; if (object.has("label")) data.label = generatedString(object.get("label"), path + ".label"); return data; }
function generatedDecodeData_Input_layout_tpl(value: Value, path: string): DefinitionData<Input_layout_tpl> { generatedDataObject(value, path); return {}; }
function generatedBindAssign(value: unknown): { assign: Assign; root: MapValue } { const root = bindMap(value); return { assign: generatedDecodeAssign(root, 'assign'), root }; }
type GeneratedTarget = { target: string | null; html?: string };
type GeneratedBoundDefinitions = { definitions: Definitions; targets: Map<string, GeneratedTarget> };
function generatedBindDefinitions(input: unknown): GeneratedBoundDefinitions {
const definitions: Definitions = {};
const targets = new Map<string, GeneratedTarget>();
if (input === undefined || input === null) return { definitions, targets };
if (!generatedPlain(input)) throw new Error('define is not an object');
const entries: [string, unknown][] = Object.entries(input);
for (const [id, raw] of entries) {
checkText(id);
switch (id) {
case "content": {
if (typeof raw === 'string') {
if (raw !== "card.tpl") throw new Error("define.content has an invalid template");
definitions.content = { template: "card.tpl" }; targets.set("content", { target: "card.tpl" }); break;
}
if (typeof raw !== 'object' || raw === null) throw new Error("define.content is not an object");
const template = 'template' in raw ? raw.template : undefined;
const html = 'html' in raw ? raw.html : undefined;
const data = 'data' in raw && raw.data !== undefined ? bindData(raw.data) : undefined;
if (typeof html === 'string') {
if (template !== undefined || data !== undefined) throw new Error("define.content has an invalid html entry");
definitions.content = { html }; targets.set("content", { target: null, html }); break;
}
if (template !== "card.tpl") throw new Error("define.content has an invalid template");
definitions.content = { template: "card.tpl", data: data === undefined ? {} : generatedDecodeData_Input_card_tpl(data, "define.content.data") }; targets.set("content", { target: "card.tpl" }); break;
}
case "layout": {
if (typeof raw === 'string') {
if (raw !== "layout.tpl") throw new Error("define.layout has an invalid template");
definitions.layout = { template: "layout.tpl" }; targets.set("layout", { target: "layout.tpl" }); break;
}
if (typeof raw !== 'object' || raw === null) throw new Error("define.layout is not an object");
const template = 'template' in raw ? raw.template : undefined;
const html = 'html' in raw ? raw.html : undefined;
const data = 'data' in raw && raw.data !== undefined ? bindData(raw.data) : undefined;
if (typeof html === 'string') {
throw new Error("define.layout has an invalid html entry");
}
if (template !== "layout.tpl") throw new Error("define.layout has an invalid template");
definitions.layout = { template: "layout.tpl", data: data === undefined ? {} : generatedDecodeData_Input_layout_tpl(data, "define.layout.data") }; targets.set("layout", { target: "layout.tpl" }); break;
}
default: throw new Error('define.' + id + ' is not declared');
}
}
return { definitions, targets };
}
function render_card_tpl(_assign: Assign, _definitions: Definitions, input: Input_card_tpl, context: RenderContext, runtime: RuntimeBindings, rootData: MapValue, scope: Scope): void {
const frame = new Frame("card.tpl", [0,30], rootData);
scope.locals.set("label", input.label);
context.at(frame, [0,16]); context.output.write("<p class=\"card\">");
context.at(frame, [16,25]); context.output.write(runtime.escape(input.label, frame, [19,24]));
context.at(frame, [25,30]); context.output.write("</p>\n");
}
function render_layout_tpl(assign: Assign, definitions: Definitions, input: Input_layout_tpl, context: RenderContext, runtime: RuntimeBindings, rootData: MapValue, scope: Scope): void {
const frame = new Frame("layout.tpl", [0,29,66,76,100,137,191,268,305,392,463,468,483,563,567,582,586,592,608,655,684,695], rootData);
const local_values_1: Array<number> = generatedChecked(generatedListOf(generatedNumber), runtime.depth([0, ...runtime.listSpread(assign.numbers, frame, [16,26])], frame, [12,27]), "[0, ...numbers]", runtime, frame, [12,27]);
scope.locals.set("values", local_values_1);
const local_merged_2: Map<string, string> = generatedChecked(generatedMapOf(generatedString), runtime.depth(new Map<string, Value>([...runtime.mapSpread(assign.lookup, frame, [42,51]), [runtime.stringify("z", frame, [53,56]), "Z"]]), frame, [41,64]), "[...lookup, \"z\" => \"Z\"]", runtime, frame, [41,64]);
scope.locals.set("merged", local_merged_2);
context.at(frame, [66,80]); context.output.write("<section>\n<h1>");
context.at(frame, [80,94]); context.output.write(runtime.escape((assign.page).title, frame, [83,93]));
context.at(frame, [94,119]); context.output.write("</h1>\n<p class=\"escaped\">");
context.at(frame, [119,132]); context.output.write(runtime.escape(assign.dangerous, frame, [122,131]));
context.at(frame, [132,156]); context.output.write("</p>\n<p class=\"logical\">");
context.at(frame, [156,171]); context.output.write(runtime.escape((() => { const left = assign.flag; return runtime.truthy(left) ? runtime.truthy("x") : false; })(), frame, [159,170]));
context.at(frame, [171,172]); context.output.write("|");
context.at(frame, [172,186]); context.output.write(runtime.escape((() => { const left = false; return runtime.truthy(left) ? true : runtime.truthy(2); })(), frame, [175,185]));
context.at(frame, [186,219]); context.output.write("</p>\n<p class=\"empty-truthiness\">");
context.at(frame, [219,241]); context.output.write(runtime.escape((() => { const left = assign.empty_list; return runtime.truthy(left) ? runtime.truthy(assign.flag) : false; })(), frame, [222,240]));
context.at(frame, [241,242]); context.output.write("|");
context.at(frame, [242,263]); context.output.write(runtime.escape((() => { const left = assign.empty_map; return runtime.truthy(left) ? runtime.truthy(assign.flag) : false; })(), frame, [245,262]));
context.at(frame, [263,271]); context.output.write("</p>\n<p>");
context.at(frame, [271,284]); context.output.write(runtime.escape(generatedChecked(generatedOptional(generatedNumber), runtime.index(local_values_1, 1, frame, [274,283]), "values[1]", runtime, frame, [274,283]), frame, [274,283]));
context.at(frame, [284,285]); context.output.write("|");
context.at(frame, [285,300]); context.output.write(runtime.escape(generatedChecked(generatedOptional(generatedString), runtime.index(local_merged_2, "z", frame, [288,299]), "merged[\"z\"]", runtime, frame, [288,299]), frame, [288,299]));
context.at(frame, [300,305]); context.output.write("</p>\n");
if (runtime.truthy((() => { const left = assign.flag; return runtime.truthy(left) ? runtime.truthy(generatedChecked(generatedBoolean, runtime.binary("==", (assign.page).title, "Guide", frame, [316,337]), "page.title == \"Guide\"", runtime, frame, [316,337])) : false; })())) {
context.at(frame, [338,362]); context.output.write("<strong>matched</strong>");
} else {
context.at(frame, [365,388]); context.output.write("<strong>missed</strong>");
}
context.at(frame, [391,395]); context.output.write("\n<p>");
context.at(frame, [395,418]); context.output.write(runtime.escape((runtime.truthy(assign.flag) ? "yes" : "no"), frame, [398,417]));
context.at(frame, [418,419]); context.output.write("|");
context.at(frame, [419,429]); context.output.write(runtime.escape(generatedChecked(generatedNumber, runtime.binary("+", generatedChecked(generatedNumber, runtime.unary("-", 1, frame, [422,424]), "-1", runtime, frame, [422,424]), 3, frame, [422,428]), "-1 + 3", runtime, frame, [422,428]), frame, [422,428]));
context.at(frame, [429,430]); context.output.write("|");
context.at(frame, [430,458]); context.output.write(runtime.escape(generatedChecked(generatedString, runtime.call("default", ["", "fallback"], frame, [433,457]), "default(\"\", \"fallback\")", runtime, frame, [433,457]), frame, [433,457]));
context.at(frame, [458,468]); context.output.write("</p>\n<ul>\n");
{ const row_entries = generatedEntries(assign.rows);
const row_size = row_entries.length; const row_last_index = row_size - 1;
const row_previous = scope.locals.get("row");
try {
for (const [row_index, [, local_row_3]] of row_entries.entries()) {
scope.locals.set("row", generatedEncode_Row(local_row_3));
const row_first = row_index === 0;
const row_last = row_index === row_last_index;
context.iterations += 1;
runtime.limit('iteration', context.iterations, frame, [468,585]);
context.at(frame, [483,487]); context.output.write("<li>");
context.at(frame, [487,501]); context.output.write(runtime.escape(row_index, frame, [490,500]));
context.at(frame, [501,502]); context.output.write("/");
context.at(frame, [502,515]); context.output.write(runtime.escape(row_size, frame, [505,514]));
context.at(frame, [515,516]); context.output.write(":");
context.at(frame, [516,528]); context.output.write(runtime.escape((local_row_3).name, frame, [519,527]));
context.at(frame, [528,529]); context.output.write(":");
context.at(frame, [529,543]); context.output.write(runtime.escape(row_first, frame, [532,542]));
context.at(frame, [543,544]); context.output.write(":");
context.at(frame, [544,557]); context.output.write(runtime.escape(row_last, frame, [547,556]));
context.at(frame, [557,563]); context.output.write("</li>\n");
}
} finally { if (row_previous !== undefined) scope.locals.set("row", row_previous); else scope.locals.delete("row"); }
if (row_size === 0) {
context.at(frame, [567,582]); context.output.write("<li>empty</li>\n");
}
}
context.at(frame, [586,592]); context.output.write("</ul>\n");
context.enter("partial.tpl", frame, [592,607]);
try { render_partial_tpl(assign, definitions, { values: local_values_1 }, context, runtime, rootData, scope); } finally { context.leave(); }
if (definitions.content !== undefined) {
context.at(frame, [620,634]); context.output.write("<p>defined</p>");
} else {
context.at(frame, [637,651]); context.output.write("<p>missing</p>");
}
context.at(frame, [654,655]); context.output.write("\n");
{ let definition = definitions.content;
if (definition === undefined) throw runtime.error(frame, [655,683], 'E_RUNTIME_BLOCK_UNDEFINED', "define content is not registered");
if (definition?.html !== undefined) { context.at(frame, [655,683]); context.output.write(definition.html); }
else { const input: Input_card_tpl = { label: (assign.page).title }; const blockScope = new Scope(); context.enter("card.tpl", frame, [655,683]); try { render_card_tpl(assign, definitions, input, context, runtime, rootData, blockScope); } finally { context.leave(); } }
}
context.at(frame, [684,695]); context.output.write("</section>\n");
}
function render_partial_tpl(_assign: Assign, _definitions: Definitions, input: Input_partial_tpl, context: RenderContext, runtime: RuntimeBindings, rootData: MapValue, scope: Scope): void {
const frame = new Frame("partial.tpl", [0,38], rootData);
scope.locals.set("values", input.values);
context.at(frame, [0,20]); context.output.write("<p class=\"included\">");
context.at(frame, [20,33]); context.output.write(runtime.escape(generatedChecked(generatedOptional(generatedNumber), runtime.index(input.values, 2, frame, [23,32]), "values[2]", runtime, frame, [23,32]), frame, [23,32]));
context.at(frame, [33,38]); context.output.write("</p>\n");
}
function renderTemplate(target: string, assign: Assign, definitions: Definitions, context: RenderContext, runtime: RuntimeBindings, rootData: MapValue, scope: Scope): void {
switch (target) {
case "layout.tpl": render_layout_tpl(assign, definitions, {}, context, runtime, rootData, scope); return;
default: throw context.fail('E_LOAD_NOT_FOUND', null, null, 'template ' + target + ' does not exist');
}
}
function generatedEnv(input: unknown): { timezone: string; now: number } { if (input === undefined || input === null) return { timezone: 'Z', now: Math.floor(Date.now() / 1000) }; if (!generatedPlain(input)) throw new BindError('E_DATA_UNSUPPORTED_TYPE', 'env is not an object'); const timezone = ('timezone' in input ? input.timezone : undefined) ?? 'Z'; const now = ('now' in input ? input.now : undefined) ?? Math.floor(Date.now() / 1000); if (typeof timezone !== 'string') throw new BindError('E_DATA_UNSUPPORTED_TYPE', 'env.timezone is not a string'); if (typeof now !== 'number') throw new BindError('E_DATA_UNSUPPORTED_TYPE', 'env.now is not a number'); return { timezone, now }; }
class GeneratedPreparedRender implements PreparedRender { private readonly name: string; private readonly execute: () => string; constructor(name: string, execute: () => string) { this.name = name; this.execute = execute; } render(): string { return internalBoundary(this.name, this.execute); } }
export class GeneratedProgram implements Program {
readonly runtime: RuntimeEnvironment;
constructor(runtime: RuntimeEnvironment = new RuntimeEnvironment()) { this.runtime = runtime; }
prepare(target: string | Template, assign: unknown, options: RenderOptions = {}): PreparedRender { if (typeof target !== 'string') throw new Error('generated target must be a template name'); return internalBoundary(target, () => this.prepareBound(target, assign, options)); }
private prepareBound(target: string, assign: unknown, options: RenderOptions): PreparedRender { let boundAssign: { assign: Assign; root: MapValue }; let bound: GeneratedBoundDefinitions; let env: { timezone: string; now: number }; try { boundAssign = generatedBindAssign(assign); bound = generatedBindDefinitions(options.define); env = generatedEnv(options.env); } catch (error) { if (error instanceof BindError) throw new TemplateError({ code: error.code, template: target, line: 0, col: 0, offset: 0, end: 0, message: error.message }); throw error; } const registered = bound.targets.get(target); const html = registered?.html; if (html !== undefined) return new GeneratedPreparedRender(target, () => html); const targetName = registered?.target ?? target; return new GeneratedPreparedRender(target, () => { const context = new RenderContext(this.runtime, boundAssign.root, env, targetName); const runtime = new RuntimeBindings(context); const scope = new Scope(); context.enter(targetName, null, null); try { renderTemplate(targetName, boundAssign.assign, bound.definitions, context, runtime, boundAssign.root, scope); return context.output.toString(); } finally { context.leave(); } }); }
render(target: string | Template, assign: unknown, options: RenderOptions = {}): string { return this.prepare(target, assign, options).render(); }
}
javascript · product generated program
{
"schema": 3,
"mode": "gen",
"target": "js",
"entry": "layout.tpl",
"sourceDigest": "936a20a5f3b77bc8a454a40c57b55de1118203b69a39aedda5acb0455f495625",
"typeDigest": "30c0b093d1694670ec7117cc02b36e0031780485e6218be94afbdf1be6524db7",
"contractDigest": "b3a300448cfa0263a9e4fb6888adb359317b6f8e0e9e7f40edc29e8c8671bf2d",
"compilerDigest": "7a758948ae54e846b8c9cb16fa604ebf4a8aa94725dd3cd63ae77c94b2fe1e05",
"files": {
"compiler-coverage.js": {
"path": "compiler-coverage.js",
"artifactDigest": "5d473ea122af5e8999d768919f4a3da38de3072735e95aee12a2f31496925442"
}
}
}
import { BindError, Frame, RenderContext, RuntimeBindings, RuntimeEnvironment, Scope, TemplateError, bindData, bindMap, checkText, internalBoundary } from "@polyspec/template";
function generatedTypeError(value, path, expected) {
return new Error(value === null || value === void 0 ? path + " is required" : path + " is not " + expected);
}
function generatedChecked(decode, value, path, runtime, frame, span) {
try {
return decode(value, path);
} catch (error) {
if (error instanceof Error) throw runtime.error(frame, span, "E_RUNTIME_TYPE", error.message);
throw error;
}
}
function generatedDataObject(value, path) {
if (value instanceof Map) return value;
throw new Error(path + " is not an object");
}
function generatedPlain(value) {
if (typeof value !== "object" || value === null) return false;
const prototype = Reflect.getPrototypeOf(value);
return prototype === Object.prototype || prototype === null;
}
function generatedObject(value, path) {
if (value instanceof Map) return value;
throw generatedTypeError(value, path, "an object");
}
function generatedString(value, path) {
if (typeof value === "string") return value;
throw generatedTypeError(value, path, "a string");
}
function generatedNumber(value, path) {
if (typeof value === "number") return value;
throw generatedTypeError(value, path, "a number");
}
function generatedBoolean(value, path) {
if (typeof value === "boolean") return value;
throw generatedTypeError(value, path, "a boolean");
}
function generatedOptional(decode) {
return (value, path) => value === null || value === void 0 ? null : decode(value, path);
}
function generatedListOf(item) {
return (value, path) => {
if (Array.isArray(value)) return value.map((element, index) => item(element, path + "[" + index + "]"));
throw generatedTypeError(value, path, "a list");
};
}
function generatedMapOf(item) {
return (value, path) => new Map([...generatedObject(value, path)].map(([key, element]) => [key, item(element, path + "." + key)]));
}
function generatedEntries(value) {
if (value === null) return [];
if (Array.isArray(value)) return value.map((item, index) => [index, item]);
return [...value];
}
function generatedDecode_Page(value, path) {
const object = generatedObject(value, path);
return { title: generatedString(object.get("title"), path + ".title") };
}
function generatedDecode_Row(value, path) {
const object = generatedObject(value, path);
return { name: generatedString(object.get("name"), path + ".name") };
}
function generatedEncode_Row(value) {
return /* @__PURE__ */ new Map([["name", value.name]]);
}
function generatedDecodeAssign(value, path) {
const object = generatedObject(value, path);
return { flag: generatedBoolean(object.get("flag"), path + ".flag"), dangerous: generatedString(object.get("dangerous"), path + ".dangerous"), empty_list: generatedListOf(generatedString)(object.get("empty_list"), path + ".empty_list"), empty_map: generatedMapOf(generatedString)(object.get("empty_map"), path + ".empty_map"), page: generatedDecode_Page(object.get("page"), path + ".page"), numbers: generatedListOf(generatedNumber)(object.get("numbers"), path + ".numbers"), lookup: generatedMapOf(generatedString)(object.get("lookup"), path + ".lookup"), rows: generatedListOf(generatedDecode_Row)(object.get("rows"), path + ".rows") };
}
function generatedDecodeData_Input_card_tpl(value, path) {
const object = generatedDataObject(value, path);
const data = {};
if (object.has("label")) data.label = generatedString(object.get("label"), path + ".label");
return data;
}
function generatedDecodeData_Input_layout_tpl(value, path) {
generatedDataObject(value, path);
return {};
}
function generatedBindAssign(value) {
const root = bindMap(value);
return { assign: generatedDecodeAssign(root, "assign"), root };
}
function generatedBindDefinitions(input) {
const definitions = {};
const targets = /* @__PURE__ */ new Map();
if (input === void 0 || input === null) return { definitions, targets };
if (!generatedPlain(input)) throw new Error("define is not an object");
const entries = Object.entries(input);
for (const [id, raw] of entries) {
checkText(id);
switch (id) {
case "content": {
if (typeof raw === "string") {
if (raw !== "card.tpl") throw new Error("define.content has an invalid template");
definitions.content = { template: "card.tpl" };
targets.set("content", { target: "card.tpl" });
break;
}
if (typeof raw !== "object" || raw === null) throw new Error("define.content is not an object");
const template = "template" in raw ? raw.template : void 0;
const html = "html" in raw ? raw.html : void 0;
const data = "data" in raw && raw.data !== void 0 ? bindData(raw.data) : void 0;
if (typeof html === "string") {
if (template !== void 0 || data !== void 0) throw new Error("define.content has an invalid html entry");
definitions.content = { html };
targets.set("content", { target: null, html });
break;
}
if (template !== "card.tpl") throw new Error("define.content has an invalid template");
definitions.content = { template: "card.tpl", data: data === void 0 ? {} : generatedDecodeData_Input_card_tpl(data, "define.content.data") };
targets.set("content", { target: "card.tpl" });
break;
}
case "layout": {
if (typeof raw === "string") {
if (raw !== "layout.tpl") throw new Error("define.layout has an invalid template");
definitions.layout = { template: "layout.tpl" };
targets.set("layout", { target: "layout.tpl" });
break;
}
if (typeof raw !== "object" || raw === null) throw new Error("define.layout is not an object");
const template = "template" in raw ? raw.template : void 0;
const html = "html" in raw ? raw.html : void 0;
const data = "data" in raw && raw.data !== void 0 ? bindData(raw.data) : void 0;
if (typeof html === "string") {
throw new Error("define.layout has an invalid html entry");
}
if (template !== "layout.tpl") throw new Error("define.layout has an invalid template");
definitions.layout = { template: "layout.tpl", data: data === void 0 ? {} : generatedDecodeData_Input_layout_tpl(data, "define.layout.data") };
targets.set("layout", { target: "layout.tpl" });
break;
}
default:
throw new Error("define." + id + " is not declared");
}
}
return { definitions, targets };
}
function render_card_tpl(_assign, _definitions, input, context, runtime, rootData, scope) {
const frame = new Frame("card.tpl", [0, 30], rootData);
scope.locals.set("label", input.label);
context.at(frame, [0, 16]);
context.output.write('<p class="card">');
context.at(frame, [16, 25]);
context.output.write(runtime.escape(input.label, frame, [19, 24]));
context.at(frame, [25, 30]);
context.output.write("</p>\n");
}
function render_layout_tpl(assign, definitions, input, context, runtime, rootData, scope) {
const frame = new Frame("layout.tpl", [0, 29, 66, 76, 100, 137, 191, 268, 305, 392, 463, 468, 483, 563, 567, 582, 586, 592, 608, 655, 684, 695], rootData);
const local_values_1 = generatedChecked(generatedListOf(generatedNumber), runtime.depth([0, ...runtime.listSpread(assign.numbers, frame, [16, 26])], frame, [12, 27]), "[0, ...numbers]", runtime, frame, [12, 27]);
scope.locals.set("values", local_values_1);
const local_merged_2 = generatedChecked(generatedMapOf(generatedString), runtime.depth(new Map([...runtime.mapSpread(assign.lookup, frame, [42, 51]), [runtime.stringify("z", frame, [53, 56]), "Z"]]), frame, [41, 64]), '[...lookup, "z" => "Z"]', runtime, frame, [41, 64]);
scope.locals.set("merged", local_merged_2);
context.at(frame, [66, 80]);
context.output.write("<section>\n<h1>");
context.at(frame, [80, 94]);
context.output.write(runtime.escape(assign.page.title, frame, [83, 93]));
context.at(frame, [94, 119]);
context.output.write('</h1>\n<p class="escaped">');
context.at(frame, [119, 132]);
context.output.write(runtime.escape(assign.dangerous, frame, [122, 131]));
context.at(frame, [132, 156]);
context.output.write('</p>\n<p class="logical">');
context.at(frame, [156, 171]);
context.output.write(runtime.escape((() => {
const left = assign.flag;
return runtime.truthy(left) ? runtime.truthy("x") : false;
})(), frame, [159, 170]));
context.at(frame, [171, 172]);
context.output.write("|");
context.at(frame, [172, 186]);
context.output.write(runtime.escape((() => {
const left = false;
return runtime.truthy(left) ? true : runtime.truthy(2);
})(), frame, [175, 185]));
context.at(frame, [186, 219]);
context.output.write('</p>\n<p class="empty-truthiness">');
context.at(frame, [219, 241]);
context.output.write(runtime.escape((() => {
const left = assign.empty_list;
return runtime.truthy(left) ? runtime.truthy(assign.flag) : false;
})(), frame, [222, 240]));
context.at(frame, [241, 242]);
context.output.write("|");
context.at(frame, [242, 263]);
context.output.write(runtime.escape((() => {
const left = assign.empty_map;
return runtime.truthy(left) ? runtime.truthy(assign.flag) : false;
})(), frame, [245, 262]));
context.at(frame, [263, 271]);
context.output.write("</p>\n<p>");
context.at(frame, [271, 284]);
context.output.write(runtime.escape(generatedChecked(generatedOptional(generatedNumber), runtime.index(local_values_1, 1, frame, [274, 283]), "values[1]", runtime, frame, [274, 283]), frame, [274, 283]));
context.at(frame, [284, 285]);
context.output.write("|");
context.at(frame, [285, 300]);
context.output.write(runtime.escape(generatedChecked(generatedOptional(generatedString), runtime.index(local_merged_2, "z", frame, [288, 299]), 'merged["z"]', runtime, frame, [288, 299]), frame, [288, 299]));
context.at(frame, [300, 305]);
context.output.write("</p>\n");
if (runtime.truthy((() => {
const left = assign.flag;
return runtime.truthy(left) ? runtime.truthy(generatedChecked(generatedBoolean, runtime.binary("==", assign.page.title, "Guide", frame, [316, 337]), 'page.title == "Guide"', runtime, frame, [316, 337])) : false;
})())) {
context.at(frame, [338, 362]);
context.output.write("<strong>matched</strong>");
} else {
context.at(frame, [365, 388]);
context.output.write("<strong>missed</strong>");
}
context.at(frame, [391, 395]);
context.output.write("\n<p>");
context.at(frame, [395, 418]);
context.output.write(runtime.escape(runtime.truthy(assign.flag) ? "yes" : "no", frame, [398, 417]));
context.at(frame, [418, 419]);
context.output.write("|");
context.at(frame, [419, 429]);
context.output.write(runtime.escape(generatedChecked(generatedNumber, runtime.binary("+", generatedChecked(generatedNumber, runtime.unary("-", 1, frame, [422, 424]), "-1", runtime, frame, [422, 424]), 3, frame, [422, 428]), "-1 + 3", runtime, frame, [422, 428]), frame, [422, 428]));
context.at(frame, [429, 430]);
context.output.write("|");
context.at(frame, [430, 458]);
context.output.write(runtime.escape(generatedChecked(generatedString, runtime.call("default", ["", "fallback"], frame, [433, 457]), 'default("", "fallback")', runtime, frame, [433, 457]), frame, [433, 457]));
context.at(frame, [458, 468]);
context.output.write("</p>\n<ul>\n");
{
const row_entries = generatedEntries(assign.rows);
const row_size = row_entries.length;
const row_last_index = row_size - 1;
const row_previous = scope.locals.get("row");
try {
for (const [row_index, [, local_row_3]] of row_entries.entries()) {
scope.locals.set("row", generatedEncode_Row(local_row_3));
const row_first = row_index === 0;
const row_last = row_index === row_last_index;
context.iterations += 1;
runtime.limit("iteration", context.iterations, frame, [468, 585]);
context.at(frame, [483, 487]);
context.output.write("<li>");
context.at(frame, [487, 501]);
context.output.write(runtime.escape(row_index, frame, [490, 500]));
context.at(frame, [501, 502]);
context.output.write("/");
context.at(frame, [502, 515]);
context.output.write(runtime.escape(row_size, frame, [505, 514]));
context.at(frame, [515, 516]);
context.output.write(":");
context.at(frame, [516, 528]);
context.output.write(runtime.escape(local_row_3.name, frame, [519, 527]));
context.at(frame, [528, 529]);
context.output.write(":");
context.at(frame, [529, 543]);
context.output.write(runtime.escape(row_first, frame, [532, 542]));
context.at(frame, [543, 544]);
context.output.write(":");
context.at(frame, [544, 557]);
context.output.write(runtime.escape(row_last, frame, [547, 556]));
context.at(frame, [557, 563]);
context.output.write("</li>\n");
}
} finally {
if (row_previous !== void 0) scope.locals.set("row", row_previous);
else scope.locals.delete("row");
}
if (row_size === 0) {
context.at(frame, [567, 582]);
context.output.write("<li>empty</li>\n");
}
}
context.at(frame, [586, 592]);
context.output.write("</ul>\n");
context.enter("partial.tpl", frame, [592, 607]);
try {
render_partial_tpl(assign, definitions, { values: local_values_1 }, context, runtime, rootData, scope);
} finally {
context.leave();
}
if (definitions.content !== void 0) {
context.at(frame, [620, 634]);
context.output.write("<p>defined</p>");
} else {
context.at(frame, [637, 651]);
context.output.write("<p>missing</p>");
}
context.at(frame, [654, 655]);
context.output.write("\n");
{
let definition = definitions.content;
if (definition === void 0) throw runtime.error(frame, [655, 683], "E_RUNTIME_BLOCK_UNDEFINED", "define content is not registered");
if (definition?.html !== void 0) {
context.at(frame, [655, 683]);
context.output.write(definition.html);
} else {
const input2 = { label: assign.page.title };
const blockScope = new Scope();
context.enter("card.tpl", frame, [655, 683]);
try {
render_card_tpl(assign, definitions, input2, context, runtime, rootData, blockScope);
} finally {
context.leave();
}
}
}
context.at(frame, [684, 695]);
context.output.write("</section>\n");
}
function render_partial_tpl(_assign, _definitions, input, context, runtime, rootData, scope) {
const frame = new Frame("partial.tpl", [0, 38], rootData);
scope.locals.set("values", input.values);
context.at(frame, [0, 20]);
context.output.write('<p class="included">');
context.at(frame, [20, 33]);
context.output.write(runtime.escape(generatedChecked(generatedOptional(generatedNumber), runtime.index(input.values, 2, frame, [23, 32]), "values[2]", runtime, frame, [23, 32]), frame, [23, 32]));
context.at(frame, [33, 38]);
context.output.write("</p>\n");
}
function renderTemplate(target, assign, definitions, context, runtime, rootData, scope) {
switch (target) {
case "layout.tpl":
render_layout_tpl(assign, definitions, {}, context, runtime, rootData, scope);
return;
default:
throw context.fail("E_LOAD_NOT_FOUND", null, null, "template " + target + " does not exist");
}
}
function generatedEnv(input) {
if (input === void 0 || input === null) return { timezone: "Z", now: Math.floor(Date.now() / 1e3) };
if (!generatedPlain(input)) throw new BindError("E_DATA_UNSUPPORTED_TYPE", "env is not an object");
const timezone = ("timezone" in input ? input.timezone : void 0) ?? "Z";
const now = ("now" in input ? input.now : void 0) ?? Math.floor(Date.now() / 1e3);
if (typeof timezone !== "string") throw new BindError("E_DATA_UNSUPPORTED_TYPE", "env.timezone is not a string");
if (typeof now !== "number") throw new BindError("E_DATA_UNSUPPORTED_TYPE", "env.now is not a number");
return { timezone, now };
}
class GeneratedPreparedRender {
name;
execute;
constructor(name, execute) {
this.name = name;
this.execute = execute;
}
render() {
return internalBoundary(this.name, this.execute);
}
}
export class GeneratedProgram {
runtime;
constructor(runtime = new RuntimeEnvironment()) {
this.runtime = runtime;
}
prepare(target, assign, options = {}) {
if (typeof target !== "string") throw new Error("generated target must be a template name");
return internalBoundary(target, () => this.prepareBound(target, assign, options));
}
prepareBound(target, assign, options) {
let boundAssign;
let bound;
let env;
try {
boundAssign = generatedBindAssign(assign);
bound = generatedBindDefinitions(options.define);
env = generatedEnv(options.env);
} catch (error) {
if (error instanceof BindError) throw new TemplateError({ code: error.code, template: target, line: 0, col: 0, offset: 0, end: 0, message: error.message });
throw error;
}
const registered = bound.targets.get(target);
const html = registered?.html;
if (html !== void 0) return new GeneratedPreparedRender(target, () => html);
const targetName = registered?.target ?? target;
return new GeneratedPreparedRender(target, () => {
const context = new RenderContext(this.runtime, boundAssign.root, env, targetName);
const runtime = new RuntimeBindings(context);
const scope = new Scope();
context.enter(targetName, null, null);
try {
renderTemplate(targetName, boundAssign.assign, bound.definitions, context, runtime, boundAssign.root, scope);
return context.output.toString();
} finally {
context.leave();
}
});
}
render(target, assign, options = {}) {
return this.prepare(target, assign, options).render();
}
}
go · product generated program
{
"schema": 3,
"mode": "gen",
"target": "go",
"entry": "layout.tpl",
"sourceDigest": "936a20a5f3b77bc8a454a40c57b55de1118203b69a39aedda5acb0455f495625",
"typeDigest": "30c0b093d1694670ec7117cc02b36e0031780485e6218be94afbdf1be6524db7",
"contractDigest": "b3a300448cfa0263a9e4fb6888adb359317b6f8e0e9e7f40edc29e8c8671bf2d",
"compilerDigest": "45312e93dc75fa9cac0af4448f4b43508dff97b90f52d7c3d1407e5852dbc284",
"files": {
"generated.go": {
"path": "generated.go",
"artifactDigest": "3e46e99df34537416e9f7a268f6ff88b7b01369b786988c096d7ee3f433f988d"
}
}
}
// Code generated by @polyspec/template-compiler. DO NOT EDIT.
package generated
import (
"bytes"
"encoding/json"
"errors"
"fmt"
template "github.com/polyspec/template/packages/go"
"github.com/polyspec/template/packages/go/ast"
"github.com/polyspec/template/packages/go/errs"
"github.com/polyspec/template/packages/go/functions"
"github.com/polyspec/template/packages/go/render"
"github.com/polyspec/template/packages/go/value"
"time"
"unicode/utf8"
)
type Page struct {
Title string `json:"title"`
}
func (r Page) TemplateValue() value.Value {
result := value.NewOrderedMap()
result.Set("title", generatedValue(r.Title))
return result
}
type Row struct {
Name string `json:"name"`
}
func (r Row) TemplateValue() value.Value {
result := value.NewOrderedMap()
result.Set("name", generatedValue(r.Name))
return result
}
type Slot struct {
Template *string `json:"template"`
Html *string `json:"html"`
}
func (r Slot) TemplateValue() value.Value {
result := value.NewOrderedMap()
result.Set("template", generatedValue(r.Template))
result.Set("html", generatedValue(r.Html))
return result
}
type Assign struct {
Flag bool `json:"flag"`
Dangerous string `json:"dangerous"`
Empty_list []string `json:"empty_list"`
Empty_map OrderedMap[string, string] `json:"empty_map"`
Page Page `json:"page"`
Numbers []float64 `json:"numbers"`
Lookup OrderedMap[string, string] `json:"lookup"`
Rows []Row `json:"rows"`
}
type Input_card_tpl struct {
Label string `json:"label"`
}
type Input_layout_tpl struct{}
type Input_partial_tpl struct {
Values []float64 `json:"values"`
}
type DefinitionData_card_tpl struct {
Label *string `json:"label"`
}
type DefinitionData_layout_tpl struct{}
type DefinitionData_partial_tpl struct {
Values *[]float64 `json:"values"`
}
type Definition[T any] struct {
Template string `json:"template"`
HTML *string `json:"html"`
Data *T `json:"data"`
}
type Definitions struct {
Content *Definition[DefinitionData_card_tpl] `json:"content"`
Layout *Definition[DefinitionData_layout_tpl] `json:"layout"`
}
type ArtifactManifest struct {
Schema int
Mode string
Target string
Entry string
SourceDigest string
TypeDigest string
ContractDigest string
Files map[string]string
}
var generatedRecordFields = map[string][]generatedField{"Page": []generatedField{{Name: "title", Type: generatedType{Kind: "string"}}}, "Row": []generatedField{{Name: "name", Type: generatedType{Kind: "string"}}}, "Slot": []generatedField{{Name: "template", Type: generatedType{Kind: "string", Optional: true}}, {Name: "html", Type: generatedType{Kind: "string", Optional: true}}}}
type OrderedEntry[K comparable, V any] struct {
Key K
Value V
}
type OrderedMap[K comparable, V any] struct {
entries []OrderedEntry[K, V]
}
func NewOrderedMap[K comparable, V any]() OrderedMap[K, V] {
return OrderedMap[K, V]{}
}
func (m *OrderedMap[K, V]) Set(key K, item V) {
for index := range m.entries {
if m.entries[index].Key == key {
m.entries[index].Value = item
return
}
}
m.entries = append(m.entries, OrderedEntry[K, V]{key, item})
}
func (m OrderedMap[K, V]) Entries() []OrderedEntry[K, V] {
return m.entries
}
func (m OrderedMap[K, V]) TemplateValue() value.Value {
result := value.NewOrderedMap()
for _, entry := range m.entries {
result.Set(fmt.Sprint(entry.Key), generatedValue(entry.Value))
}
return result
}
func (m *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
decoder := json.NewDecoder(bytes.NewReader(data))
token, err := decoder.Token()
if err != nil {
return err
}
if token != json.Delim('{') {
return fmt.Errorf("generated ordered map must be an object")
}
m.entries = nil
for decoder.More() {
rawKey, err := decoder.Token()
if err != nil {
return err
}
keyText, ok := rawKey.(string)
if !ok {
return fmt.Errorf("generated ordered map key is not text")
}
keyJSON, err := json.Marshal(keyText)
if err != nil {
return err
}
var key K
if err := json.Unmarshal(keyJSON, &key); err != nil {
return err
}
var item V
if err := decoder.Decode(&item); err != nil {
return err
}
m.Set(key, item)
}
_, err = decoder.Token()
return err
}
func generatedPanic(err error) {
if err != nil {
panic(err)
}
}
func generatedValue(input any) value.Value {
switch item := input.(type) {
case nil:
return nil
case bool:
return item
case float64:
return item
case string:
return item
case value.SafeString:
return item
case value.List:
result := make(value.List, len(item))
for index, element := range item {
result[index] = generatedValue(element)
}
return result
case *value.OrderedMap:
return item
}
result, err := value.Convert(input)
generatedPanic(err)
return result
}
func generatedResult[T any](input value.Value) T {
result, err := generatedAs[T](input)
generatedPanic(err)
return result
}
func generatedAs[T any](input value.Value) (T, error) {
if result, ok := input.(T); ok {
return result, nil
}
var result T
err := generatedDecode(input, &result)
return result, err
}
func generatedTruthy(runtime *render.RuntimeBindings, input any) bool {
return runtime.Truthy(generatedValue(input))
}
func generatedUnary[T any](runtime *render.RuntimeBindings, operator string, input any, frame *render.Frame, span ast.Span) T {
result, err := runtime.Unary(operator, generatedValue(input), frame, span)
generatedPanic(err)
return generatedResult[T](result)
}
func generatedBinary[T any](runtime *render.RuntimeBindings, operator string, left, right any, frame *render.Frame, span ast.Span) T {
result, err := runtime.Binary(operator, generatedValue(left), generatedValue(right), frame, span)
generatedPanic(err)
return generatedResult[T](result)
}
func generatedIndex[T any](runtime *render.RuntimeBindings, input, key any, frame *render.Frame, span ast.Span) T {
result, err := runtime.Index(generatedValue(input), generatedValue(key), frame, span)
generatedPanic(err)
return generatedResult[T](result)
}
func generatedDepth[T any](runtime *render.RuntimeBindings, input T, frame *render.Frame, span ast.Span) T {
_, err := runtime.Depth(generatedValue(input), frame, span)
generatedPanic(err)
return input
}
func generatedCall[T any](runtime *render.RuntimeBindings, name string, args []value.Value, frame *render.Frame, span ast.Span) T {
result, err := runtime.Call(name, args, frame, span)
generatedPanic(err)
return generatedResult[T](result)
}
func generatedEscape(runtime *render.RuntimeBindings, input any, frame *render.Frame, span ast.Span) string {
result, err := runtime.Escape(generatedValue(input), frame, span)
generatedPanic(err)
return result
}
type generatedEntry[T any] struct {
Key value.Value
Value T
}
func generatedListEntries[T any](items []T) []generatedEntry[T] {
result := make([]generatedEntry[T], len(items))
for index, item := range items {
result[index] = generatedEntry[T]{float64(index), item}
}
return result
}
func generatedListSpread[T any](runtime *render.RuntimeBindings, input any, frame *render.Frame, span ast.Span) []T {
source, err := runtime.ListSpread(generatedValue(input), frame, span)
generatedPanic(err)
result := make([]T, len(source))
for index, item := range source {
result[index] = generatedResult[T](item)
}
return result
}
func generatedMapSpread[K comparable, V any](runtime *render.RuntimeBindings, input any, frame *render.Frame, span ast.Span) OrderedMap[K, V] {
source, err := runtime.MapSpread(generatedValue(input), frame, span)
generatedPanic(err)
result := NewOrderedMap[K, V]()
for _, key := range source.Keys() {
result.Set(generatedResult[K](key), generatedResult[V](source.MustGet(key)))
}
return result
}
func generatedLoopMeta(scope *render.Scope, name string) *render.LoopMeta {
result := scope.LoopMeta(name)
if result == nil {
panic("generated loop metadata is missing")
}
return result
}
func generatedWrite(context *render.Context, text string, frame *render.Frame, span ast.Span) {
generatedPanic(context.Write(text, frame, &span))
}
func generatedEnter(context *render.Context, name string, frame *render.Frame, span ast.Span) {
generatedPanic(context.Enter(name, frame, &span))
}
func generatedLimit(runtime *render.RuntimeBindings, kind string, count int, frame *render.Frame, span ast.Span) {
generatedPanic(runtime.Limit(kind, count, frame, span))
}
func generatedPlain(input any) any {
switch item := input.(type) {
case *value.OrderedMap:
result := map[string]any{}
for _, key := range item.Keys() {
entry, _ := item.Get(key)
result[key] = generatedPlain(entry)
}
return result
case value.List:
result := make([]any, len(item))
for index, entry := range item {
result[index] = generatedPlain(entry)
}
return result
default:
return input
}
}
type generatedType struct {
Kind string
Optional bool
Name string
Item *generatedType
Key *generatedType
Value *generatedType
}
type generatedField struct {
Name string
Type generatedType
}
func generatedCheck(input value.Value, kind generatedType, records map[string][]generatedField, path string) error {
if input == nil {
if kind.Optional || kind.Kind == "any" {
return nil
}
return fmt.Errorf("%s is required", path)
}
actual := value.TypeOf(input)
switch kind.Kind {
case "any":
return nil
case "string":
if actual != value.TypeString {
return fmt.Errorf("%s is not a string", path)
}
case "number":
if actual != value.TypeNumber {
return fmt.Errorf("%s is not a number", path)
}
case "boolean":
if actual != value.TypeBool {
return fmt.Errorf("%s is not a boolean", path)
}
case "list":
items, ok := input.(value.List)
if !ok {
return fmt.Errorf("%s is not a list", path)
}
for index, item := range items {
if err := generatedCheck(item, *kind.Item, records, fmt.Sprintf("%s[%d]", path, index)); err != nil {
return err
}
}
case "map":
entries, ok := input.(*value.OrderedMap)
if !ok {
return fmt.Errorf("%s is not an object", path)
}
for _, key := range entries.Keys() {
item, _ := entries.Get(key)
if err := generatedCheck(item, *kind.Value, records, path+"."+key); err != nil {
return err
}
}
default:
entries, ok := input.(*value.OrderedMap)
if !ok {
return fmt.Errorf("%s is not an object", path)
}
return generatedCheckFields(entries, records[kind.Name], records, path)
}
return nil
}
func generatedCheckFields(input *value.OrderedMap, fields []generatedField, records map[string][]generatedField, path string) error {
for _, field := range fields {
item, _ := input.Get(field.Name)
if err := generatedCheck(item, field.Type, records, path+"."+field.Name); err != nil {
return err
}
}
return nil
}
func generatedDecode(input any, output any) error {
data, err := json.Marshal(generatedPlain(input))
if err != nil {
return err
}
return json.Unmarshal(data, output)
}
func generatedEnv(options template.RenderOptions) functions.Env {
env := functions.Env{Timezone: "Z", Now: float64(time.Now().Unix())}
if options.Env != nil {
if options.Env.Timezone != "" {
env.Timezone = options.Env.Timezone
}
env.Now = options.Env.Now
}
return env
}
func render_card_tpl(assign Assign, definitions *Definitions, input Input_card_tpl, context *render.Context, runtime *render.RuntimeBindings, rootData *value.OrderedMap, scope *render.Scope) {
frame := render.NewFrame("card.tpl", errs.LineIndex{0, 30}, rootData)
scope.Locals["label"] = generatedValue(input.Label)
generatedWrite(context, "<p class=\"card\">", frame, ast.Span{0, 16})
generatedWrite(context, generatedEscape(runtime, input.Label, frame, ast.Span{19, 24}), frame, ast.Span{16, 25})
generatedWrite(context, "</p>\u000a", frame, ast.Span{25, 30})
}
func render_layout_tpl(assign Assign, definitions *Definitions, input Input_layout_tpl, context *render.Context, runtime *render.RuntimeBindings, rootData *value.OrderedMap, scope *render.Scope) {
frame := render.NewFrame("layout.tpl", errs.LineIndex{0, 29, 66, 76, 100, 137, 191, 268, 305, 392, 463, 468, 483, 563, 567, 582, 586, 592, 608, 655, 684, 695}, rootData)
var local_values_1 []float64 = generatedDepth(runtime, func() []float64 {
result := []float64{}
result = append(result, float64(0))
result = append(result, generatedListSpread[float64](runtime, assign.Numbers, frame, ast.Span{16, 26})...)
return result
}(), frame, ast.Span{12, 27})
scope.Locals["values"] = generatedValue(local_values_1)
var local_merged_2 OrderedMap[string, string] = generatedDepth(runtime, func() OrderedMap[string, string] {
result := NewOrderedMap[string, string]()
for _, entry := range generatedMapSpread[string, string](runtime, assign.Lookup, frame, ast.Span{42, 51}).Entries() {
result.Set(entry.Key, entry.Value)
}
result.Set("z", "Z")
return result
}(), frame, ast.Span{41, 64})
scope.Locals["merged"] = generatedValue(local_merged_2)
generatedWrite(context, "<section>\u000a<h1>", frame, ast.Span{66, 80})
generatedWrite(context, generatedEscape(runtime, assign.Page.Title, frame, ast.Span{83, 93}), frame, ast.Span{80, 94})
generatedWrite(context, "</h1>\u000a<p class=\"escaped\">", frame, ast.Span{94, 119})
generatedWrite(context, generatedEscape(runtime, assign.Dangerous, frame, ast.Span{122, 131}), frame, ast.Span{119, 132})
generatedWrite(context, "</p>\u000a<p class=\"logical\">", frame, ast.Span{132, 156})
generatedWrite(context, generatedEscape(runtime, func() bool {
left := assign.Flag
if !generatedTruthy(runtime, left) {
return false
}
return generatedTruthy(runtime, "x")
}(), frame, ast.Span{159, 170}), frame, ast.Span{156, 171})
generatedWrite(context, "|", frame, ast.Span{171, 172})
generatedWrite(context, generatedEscape(runtime, func() bool {
left := false
if generatedTruthy(runtime, left) {
return true
}
return generatedTruthy(runtime, float64(2))
}(), frame, ast.Span{175, 185}), frame, ast.Span{172, 186})
generatedWrite(context, "</p>\u000a<p class=\"empty-truthiness\">", frame, ast.Span{186, 219})
generatedWrite(context, generatedEscape(runtime, func() bool {
left := assign.Empty_list
if !generatedTruthy(runtime, left) {
return false
}
return generatedTruthy(runtime, assign.Flag)
}(), frame, ast.Span{222, 240}), frame, ast.Span{219, 241})
generatedWrite(context, "|", frame, ast.Span{241, 242})
generatedWrite(context, generatedEscape(runtime, func() bool {
left := assign.Empty_map
if !generatedTruthy(runtime, left) {
return false
}
return generatedTruthy(runtime, assign.Flag)
}(), frame, ast.Span{245, 262}), frame, ast.Span{242, 263})
generatedWrite(context, "</p>\u000a<p>", frame, ast.Span{263, 271})
generatedWrite(context, generatedEscape(runtime, generatedIndex[*float64](runtime, local_values_1, float64(1), frame, ast.Span{274, 283}), frame, ast.Span{274, 283}), frame, ast.Span{271, 284})
generatedWrite(context, "|", frame, ast.Span{284, 285})
generatedWrite(context, generatedEscape(runtime, generatedIndex[*string](runtime, local_merged_2, "z", frame, ast.Span{288, 299}), frame, ast.Span{288, 299}), frame, ast.Span{285, 300})
generatedWrite(context, "</p>\u000a", frame, ast.Span{300, 305})
if generatedTruthy(runtime, func() bool {
left := assign.Flag
if !generatedTruthy(runtime, left) {
return false
}
return generatedTruthy(runtime, generatedBinary[bool](runtime, "==", assign.Page.Title, "Guide", frame, ast.Span{316, 337}))
}()) {
generatedWrite(context, "<strong>matched</strong>", frame, ast.Span{338, 362})
} else {
generatedWrite(context, "<strong>missed</strong>", frame, ast.Span{365, 388})
}
generatedWrite(context, "\u000a<p>", frame, ast.Span{391, 395})
generatedWrite(context, generatedEscape(runtime, func() string {
if generatedTruthy(runtime, assign.Flag) {
return "yes"
}
return "no"
}(), frame, ast.Span{398, 417}), frame, ast.Span{395, 418})
generatedWrite(context, "|", frame, ast.Span{418, 419})
generatedWrite(context, generatedEscape(runtime, generatedBinary[float64](runtime, "+", generatedUnary[float64](runtime, "-", float64(1), frame, ast.Span{422, 424}), float64(3), frame, ast.Span{422, 428}), frame, ast.Span{422, 428}), frame, ast.Span{419, 429})
generatedWrite(context, "|", frame, ast.Span{429, 430})
generatedWrite(context, generatedEscape(runtime, generatedCall[string](runtime, "default", []value.Value{generatedValue(""), generatedValue("fallback")}, frame, ast.Span{433, 457}), frame, ast.Span{433, 457}), frame, ast.Span{430, 458})
generatedWrite(context, "</p>\u000a<ul>\u000a", frame, ast.Span{458, 468})
{
entries := generatedListEntries(assign.Rows)
entriesSize := len(entries)
lastIndex := entriesSize - 1
previous, hadPrevious := scope.Locals["row"]
for row_index, entry := range entries {
row_key, local_row_3 := entry.Key, entry.Value
row_value := generatedValue(local_row_3)
scope.Locals["row"] = row_value
scope.Loops["row"] = append(scope.Loops["row"], &render.LoopMeta{Index: row_index, Key: row_key, Value: row_value, Size: entriesSize, First: row_index == 0, Last: row_index == lastIndex})
context.Iterations++
generatedLimit(runtime, "iteration", context.Iterations, frame, ast.Span{468, 585})
generatedWrite(context, "<li>", frame, ast.Span{483, 487})
generatedWrite(context, generatedEscape(runtime, generatedLoopMeta(scope, "row").Index, frame, ast.Span{490, 500}), frame, ast.Span{487, 501})
generatedWrite(context, "/", frame, ast.Span{501, 502})
generatedWrite(context, generatedEscape(runtime, generatedLoopMeta(scope, "row").Size, frame, ast.Span{505, 514}), frame, ast.Span{502, 515})
generatedWrite(context, ":", frame, ast.Span{515, 516})
generatedWrite(context, generatedEscape(runtime, local_row_3.Name, frame, ast.Span{519, 527}), frame, ast.Span{516, 528})
generatedWrite(context, ":", frame, ast.Span{528, 529})
generatedWrite(context, generatedEscape(runtime, generatedLoopMeta(scope, "row").First, frame, ast.Span{532, 542}), frame, ast.Span{529, 543})
generatedWrite(context, ":", frame, ast.Span{543, 544})
generatedWrite(context, generatedEscape(runtime, generatedLoopMeta(scope, "row").Last, frame, ast.Span{547, 556}), frame, ast.Span{544, 557})
generatedWrite(context, "</li>\u000a", frame, ast.Span{557, 563})
scope.Loops["row"] = scope.Loops["row"][:len(scope.Loops["row"])-1]
}
if hadPrevious {
scope.Locals["row"] = previous
} else {
delete(scope.Locals, "row")
}
if entriesSize == 0 {
generatedWrite(context, "<li>empty</li>\u000a", frame, ast.Span{567, 582})
}
}
generatedWrite(context, "</ul>\u000a", frame, ast.Span{586, 592})
generatedEnter(context, "partial.tpl", frame, ast.Span{592, 607})
func() {
defer context.Leave()
render_partial_tpl(assign, definitions, Input_partial_tpl{Values: local_values_1}, context, runtime, rootData, scope)
}()
if definitions.Content != nil {
generatedWrite(context, "<p>defined</p>", frame, ast.Span{620, 634})
} else {
generatedWrite(context, "<p>missing</p>", frame, ast.Span{637, 651})
}
generatedWrite(context, "\u000a", frame, ast.Span{654, 655})
{
definition := definitions.Content
if definition == nil {
panic(runtime.Error(frame, ast.Span{655, 683}, errs.RuntimeBlockUndefined, "define content is not registered"))
}
if definition != nil && definition.HTML != nil {
generatedWrite(context, *definition.HTML, frame, ast.Span{655, 683})
} else {
input := Input_card_tpl{}
if definition != nil && definition.Data != nil {
if definition.Data.Label != nil {
input.Label = *definition.Data.Label
}
}
input.Label = assign.Page.Title
blockScope := render.NewScope()
generatedEnter(context, "card.tpl", frame, ast.Span{655, 683})
func() {
defer context.Leave()
render_card_tpl(assign, definitions, input, context, runtime, rootData, blockScope)
}()
}
}
generatedWrite(context, "</section>\u000a", frame, ast.Span{684, 695})
}
func render_partial_tpl(assign Assign, definitions *Definitions, input Input_partial_tpl, context *render.Context, runtime *render.RuntimeBindings, rootData *value.OrderedMap, scope *render.Scope) {
frame := render.NewFrame("partial.tpl", errs.LineIndex{0, 38}, rootData)
scope.Locals["values"] = generatedValue(input.Values)
generatedWrite(context, "<p class=\"included\">", frame, ast.Span{0, 20})
generatedWrite(context, generatedEscape(runtime, generatedIndex[*float64](runtime, input.Values, float64(2), frame, ast.Span{23, 32}), frame, ast.Span{23, 32}), frame, ast.Span{20, 33})
generatedWrite(context, "</p>\u000a", frame, ast.Span{33, 38})
}
func renderTemplate(target string, assign Assign, definitions *Definitions, context *render.Context, runtime *render.RuntimeBindings, rootData *value.OrderedMap, scope *render.Scope) {
switch target {
case "layout.tpl":
render_layout_tpl(assign, definitions, Input_layout_tpl{}, context, runtime, rootData, scope)
return
default:
panic(context.Fail(errs.LoadNotFound, nil, nil, "template "+target+" does not exist"))
}
}
type generatedTarget struct {
target string
html *string
}
func generatedBindDefinitions(input map[string]template.DefineInput) (*Definitions, map[string]generatedTarget, error) {
definitions := &Definitions{}
targets := map[string]generatedTarget{}
for id, input := range input {
if !utf8.ValidString(id) {
return nil, nil, &value.BindError{Code: errs.DataInvalidUTF8, Message: "a define id is not valid UTF-8"}
}
switch id {
case "content":
if input.HTML != nil {
if input.Template != "" || input.Data != nil {
return nil, nil, fmt.Errorf("define.%s has an invalid html entry", id)
}
definitions.Content = &Definition[DefinitionData_card_tpl]{HTML: input.HTML}
targets[id] = generatedTarget{html: input.HTML}
continue
}
if input.Template != "card.tpl" {
return nil, nil, fmt.Errorf("define.%s has an invalid template", id)
}
entry := &Definition[DefinitionData_card_tpl]{Template: input.Template}
if input.Data != nil {
bound, err := value.BindData(input.Data)
if err != nil {
return nil, nil, err
}
data, ok := bound.(*value.OrderedMap)
if !ok {
return nil, nil, fmt.Errorf("define.%s.data is not an object", id)
}
definitionData := DefinitionData_card_tpl{}
if raw, found := data.Get("label"); found {
if err := generatedCheck(raw, generatedType{Kind: "string"}, generatedRecordFields, "define."+id+".data."+"label"); err != nil {
return nil, nil, err
}
converted, err := generatedAs[string](raw)
if err != nil {
return nil, nil, fmt.Errorf("define.%s.data.%s: %w", id, "label", err)
}
definitionData.Label = &converted
}
entry.Data = &definitionData
}
definitions.Content = entry
targets[id] = generatedTarget{target: "card.tpl"}
case "layout":
if input.HTML != nil {
return nil, nil, fmt.Errorf("define.%s has an invalid html entry", id)
}
if input.Template != "layout.tpl" {
return nil, nil, fmt.Errorf("define.%s has an invalid template", id)
}
entry := &Definition[DefinitionData_layout_tpl]{Template: input.Template}
if input.Data != nil {
bound, err := value.BindData(input.Data)
if err != nil {
return nil, nil, err
}
if _, ok := bound.(*value.OrderedMap); !ok {
return nil, nil, fmt.Errorf("define.%s.data is not an object", id)
}
definitionData := DefinitionData_layout_tpl{}
entry.Data = &definitionData
}
definitions.Layout = entry
targets[id] = generatedTarget{target: "layout.tpl"}
default:
return nil, nil, fmt.Errorf("define.%s is not declared", id)
}
}
return definitions, targets, nil
}
type GeneratedProgram struct {
Runtime *template.RuntimeEnvironment
}
func NewGeneratedProgram(options template.Options) (*GeneratedProgram, error) {
runtime, err := template.NewRuntimeEnvironment(options.Limits, options.Functions)
if err != nil {
return nil, err
}
return &GeneratedProgram{Runtime: runtime}, nil
}
type generatedPrepared struct {
name string
target string
assign Assign
definitions *Definitions
rootData *value.OrderedMap
env functions.Env
runtime *template.RuntimeEnvironment
html *string
}
func (p *generatedPrepared) Render() (string, error) {
return render.Guard(p.name, p.render)
}
func (p *generatedPrepared) render() (string, error) {
context := render.NewContext(p.runtime, p.rootData, p.env, p.target)
runtime := render.NewRuntimeBindings(context)
scope := render.NewScope()
if err := context.Enter(p.target, nil, nil); err != nil {
return "", err
}
defer context.Leave()
if p.html != nil {
if err := context.Write(*p.html, nil, nil); err != nil {
return "", err
}
} else {
renderTemplate(p.target, p.assign, p.definitions, context, runtime, p.rootData, scope)
}
return context.Output(), nil
}
func (p *GeneratedProgram) Prepare(target any, assign any, options template.RenderOptions) (template.Prepared, error) {
name, ok := target.(string)
if !ok {
return nil, fmt.Errorf("generated target must be a template name")
}
return render.Guard(name, func() (template.Prepared, error) {
return p.prepare(name, assign, options)
})
}
func generatedBindFailure(name string, err error) error {
var bindError *value.BindError
if errors.As(err, &bindError) {
return errs.WithoutPosition(bindError.Code, name, bindError.Message)
}
return err
}
func (p *GeneratedProgram) prepare(name string, assign any, options template.RenderOptions) (template.Prepared, error) {
rootData, err := value.BindMap(assign)
if err != nil {
return nil, generatedBindFailure(name, err)
}
var typedAssign Assign
if err := generatedCheckFields(rootData, []generatedField{{Name: "flag", Type: generatedType{Kind: "boolean"}}, {Name: "dangerous", Type: generatedType{Kind: "string"}}, {Name: "empty_list", Type: generatedType{Kind: "list", Item: &generatedType{Kind: "string"}}}, {Name: "empty_map", Type: generatedType{Kind: "map", Key: &generatedType{Kind: "string"}, Value: &generatedType{Kind: "string"}}}, {Name: "page", Type: generatedType{Kind: "record", Name: "Page"}}, {Name: "numbers", Type: generatedType{Kind: "list", Item: &generatedType{Kind: "number"}}}, {Name: "lookup", Type: generatedType{Kind: "map", Key: &generatedType{Kind: "string"}, Value: &generatedType{Kind: "string"}}}, {Name: "rows", Type: generatedType{Kind: "list", Item: &generatedType{Kind: "record", Name: "Row"}}}}, generatedRecordFields, "assign"); err != nil {
return nil, err
}
if err := generatedDecode(rootData, &typedAssign); err != nil {
return nil, err
}
definitions, targets, err := generatedBindDefinitions(options.Define)
if err != nil {
return nil, generatedBindFailure(name, err)
}
resolved := targets[name]
targetName := name
if resolved.target != "" {
targetName = resolved.target
}
return &generatedPrepared{name: name, target: targetName, assign: typedAssign, definitions: definitions, rootData: rootData, env: generatedEnv(options), runtime: p.Runtime, html: resolved.html}, nil
}
func (p *GeneratedProgram) Render(target any, assign any, options template.RenderOptions) (string, error) {
prepared, err := p.Prepare(target, assign, options)
if err != nil {
return "", err
}
return prepared.Render()
}
var _ template.Program = (*GeneratedProgram)(nil)
rust · product generated program
{
"schema": 3,
"mode": "gen",
"target": "rust",
"entry": "layout.tpl",
"sourceDigest": "936a20a5f3b77bc8a454a40c57b55de1118203b69a39aedda5acb0455f495625",
"typeDigest": "30c0b093d1694670ec7117cc02b36e0031780485e6218be94afbdf1be6524db7",
"contractDigest": "b3a300448cfa0263a9e4fb6888adb359317b6f8e0e9e7f40edc29e8c8671bf2d",
"compilerDigest": "88d74f055ec030758070b516d0cd35473b90e243ae8571128c3187ba0162631f",
"files": {
"compiler-coverage.rust": {
"path": "compiler-coverage.rust",
"artifactDigest": "02a59e563c9a631b45a902749a27d20611407108b112fb622ddc077c92eb4e6f"
}
}
}
// Generated.
use polyspec_template::{ArgumentError, ErrorCode, OrderedMap as RuntimeOrderedMap, PreparedRender, Program, RenderOptions, RenderTarget, RequestError, RuntimeEnvironment, TemplateError, Value, internal_boundary};
use polyspec_template::error::LineIndex;
use polyspec_template::render::context::{Frame, RenderContext, Scope};
use polyspec_template::render::runtime_bindings::RuntimeBindings;
use serde_json::Value as JsonValue;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Clone, Default)] pub struct Assign {
pub flag: bool,
pub dangerous: String,
pub empty_list: Vec<String>,
pub empty_map: OrderedMap<String, String>,
pub page: Page,
pub numbers: Vec<f64>,
pub lookup: OrderedMap<String, String>,
pub rows: Vec<Row>,
}
impl GeneratedConvert for Assign { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { let Value::Map(map) = input else { return Err(generated_type_error(input, path, "an object")); }; let _ = (&map, path); let flag = generated_field(map, path, "flag")?; let dangerous = generated_field(map, path, "dangerous")?; let empty_list = generated_field(map, path, "empty_list")?; let empty_map = generated_field(map, path, "empty_map")?; let page = generated_field(map, path, "page")?; let numbers = generated_field(map, path, "numbers")?; let lookup = generated_field(map, path, "lookup")?; let rows = generated_field(map, path, "rows")?; Ok(Assign { flag, dangerous, empty_list, empty_map, page, numbers, lookup, rows }) } fn to_value(&self) -> Value { let mut map = RuntimeOrderedMap::new(); map.insert("flag".to_string(), self.flag.to_value()); map.insert("dangerous".to_string(), self.dangerous.to_value()); map.insert("empty_list".to_string(), self.empty_list.to_value()); map.insert("empty_map".to_string(), self.empty_map.to_value()); map.insert("page".to_string(), self.page.to_value()); map.insert("numbers".to_string(), self.numbers.to_value()); map.insert("lookup".to_string(), self.lookup.to_value()); map.insert("rows".to_string(), self.rows.to_value()); Value::map(map) } }
#[derive(Clone, Default)] pub struct InputCardTpl { pub label: String }
#[derive(Clone, Default)] pub struct InputLayoutTpl { }
#[derive(Clone, Default)] pub struct InputPartialTpl { pub values: Vec<f64> }
#[derive(Clone, Default)] pub struct DefinitionDataCardTpl { pub label: Option<String> }
#[derive(Clone, Default)] pub struct DefinitionDataLayoutTpl { }
#[derive(Clone, Default)] pub struct Definition<T> { pub html: Option<String>, pub data: Option<T> }
#[derive(Clone, Default)] pub struct Definitions { pub content: Option<Definition<DefinitionDataCardTpl>>, pub layout: Option<Definition<DefinitionDataLayoutTpl>> }
#[derive(Clone, Default)] pub struct Page { pub title: String }
impl GeneratedConvert for Page { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { let Value::Map(map) = input else { return Err(generated_type_error(input, path, "an object")); }; let _ = (&map, path); let title = generated_field(map, path, "title")?; Ok(Page { title }) } fn to_value(&self) -> Value { let mut map = RuntimeOrderedMap::new(); map.insert("title".to_string(), self.title.to_value()); Value::map(map) } }
#[derive(Clone, Default)] pub struct Row { pub name: String }
impl GeneratedConvert for Row { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { let Value::Map(map) = input else { return Err(generated_type_error(input, path, "an object")); }; let _ = (&map, path); let name = generated_field(map, path, "name")?; Ok(Row { name }) } fn to_value(&self) -> Value { let mut map = RuntimeOrderedMap::new(); map.insert("name".to_string(), self.name.to_value()); Value::map(map) } }
#[derive(Clone)] struct OrderedEntry<K, V> { key: K, value: V }
#[derive(Clone, Default)] pub struct OrderedMap<K, V> { entries: Vec<OrderedEntry<K, V>> }
impl<K: PartialEq, V> OrderedMap<K, V> { fn new() -> Self { Self { entries: Vec::new() } } fn set(&mut self, key: K, value: V) { if let Some(entry) = self.entries.iter_mut().find(|entry| entry.key == key) { entry.value = value; } else { self.entries.push(OrderedEntry { key, value }); } } }
impl<K: GeneratedConvert + PartialEq, V: GeneratedConvert> GeneratedConvert for OrderedMap<K, V> { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { let Value::Map(map) = input else { return Err(generated_type_error(input, path, "an object")); }; let mut result = OrderedMap::new(); for (key, value) in map.iter() { result.set(K::from_value(&Value::text(key.clone()), path)?, V::from_value(value, &format!("{path}.{key}"))?); } Ok(result) } fn to_value(&self) -> Value { let mut map = RuntimeOrderedMap::new(); for entry in &self.entries { map.insert(entry.key.to_value().as_text().unwrap_or_default().to_string(), entry.value.to_value()); } Value::map(map) } }
fn generated_conversion_error(message: impl Into<String>) -> TemplateError { TemplateError::without_position(ErrorCode::E_RUNTIME_TYPE, "generated", message) }
pub trait GeneratedConvert: Sized { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError>; fn to_value(&self) -> Value; }
impl GeneratedConvert for Value { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { let _ = path; Ok(input.clone()) } fn to_value(&self) -> Value { self.clone() } }
impl GeneratedConvert for String { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { input.as_text().map(str::to_string).ok_or_else(|| generated_type_error(input, path, "a string")) } fn to_value(&self) -> Value { Value::text(self.clone()) } }
impl GeneratedConvert for f64 { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { match input { Value::Number(number) => Ok(*number), _ => Err(generated_type_error(input, path, "a number")) } } fn to_value(&self) -> Value { Value::Number(*self) } }
impl GeneratedConvert for bool { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { match input { Value::Bool(value) => Ok(*value), _ => Err(generated_type_error(input, path, "a boolean")) } } fn to_value(&self) -> Value { Value::Bool(*self) } }
impl<T: GeneratedConvert> GeneratedConvert for Option<T> { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { match input { Value::Null => Ok(None), other => T::from_value(other, path).map(Some) } } fn to_value(&self) -> Value { self.as_ref().map_or(Value::Null, T::to_value) } }
impl<T: GeneratedConvert> GeneratedConvert for Vec<T> { fn from_value(input: &Value, path: &str) -> Result<Self, TemplateError> { match input { Value::List(items) => items.iter().enumerate().map(|(index, item)| T::from_value(item, &format!("{path}[{index}]"))).collect(), _ => Err(generated_type_error(input, path, "a list")) } } fn to_value(&self) -> Value { Value::list(self.iter().map(T::to_value).collect()) } }
fn generated_type_error(input: &Value, path: &str, expected: &str) -> TemplateError { generated_conversion_error(if matches!(input, Value::Null) { format!("{path} is required") } else { format!("{path} is not {expected}") }) }
fn generated_field<T: GeneratedConvert>(map: &RuntimeOrderedMap, path: &str, name: &str) -> Result<T, TemplateError> { T::from_value(map.get(name).unwrap_or(&Value::Null), &format!("{path}.{name}")) }
fn generated_value<T: GeneratedConvert>(input: &T) -> Result<Value, TemplateError> { Ok(input.to_value()) }
fn generated_result<T: GeneratedConvert>(input: &Value, path: &str) -> Result<T, TemplateError> { T::from_value(input, path) }
fn generated_loop_meta(runtime: &RuntimeBindings, context: &RenderContext<'_>, scope: &Scope, frame: &Frame, span: polyspec_template::error::Span, name: &str, field: &str) -> Result<Value, TemplateError> { let meta = scope.loop_meta(name).ok_or_else(|| runtime.error(context, frame, span, ErrorCode::E_RUNTIME_UNKNOWN_LOOP, format!("loop {name} is not active")))?; Ok(match field { "index_" => Value::Number(meta.index as f64), "size_" => Value::Number(meta.size as f64), "first_" => Value::Bool(meta.first), "last_" => Value::Bool(meta.last), "key_" => meta.key.clone(), "value_" => meta.value.clone(), _ => return Err(runtime.error(context, frame, span, ErrorCode::E_RUNTIME_UNKNOWN_LOOP, format!("loop {name} has no metadata {field}"))) }) }
fn render_card_tpl(assign: &Assign, definitions: &mut Definitions, input: InputCardTpl, context: &mut RenderContext<'_>, runtime: &RuntimeBindings, root_data: &Rc<RuntimeOrderedMap>, scope: &mut Scope) -> Result<(), TemplateError> { let _ = (&assign, &definitions, &input, &runtime, &scope); let frame = Frame { name: "card.tpl".to_string(), lines: Some(LineIndex::from_starts(vec![0, 30])), context: Rc::clone(root_data) };
scope.locals.insert("label".to_string(), generated_value(&input.label)?);
context.write("<p class=\"card\">", &frame, [0, 16])?;
let escaped = runtime.escape(context, &scope.lookup(&frame, "label"), &frame, [19, 24])?; context.write(&escaped, &frame, [16, 25])?;
context.write("</p>\u{a}", &frame, [25, 30])?;
Ok(()) }
fn render_layout_tpl(assign: &Assign, definitions: &mut Definitions, input: InputLayoutTpl, context: &mut RenderContext<'_>, runtime: &RuntimeBindings, root_data: &Rc<RuntimeOrderedMap>, scope: &mut Scope) -> Result<(), TemplateError> { let _ = (&assign, &definitions, &input, &runtime, &scope); let frame = Frame { name: "layout.tpl".to_string(), lines: Some(LineIndex::from_starts(vec![0, 29, 66, 76, 100, 137, 191, 268, 305, 392, 463, 468, 483, 563, 567, 582, 586, 592, 608, 655, 684, 695])), context: Rc::clone(root_data) };
scope.locals.insert("values".to_string(), { let mut result = Vec::new(); result.push(Value::Number(0f64)); result.extend(runtime.list_spread(context, &generated_value(&assign.numbers)?, &frame, [16, 26])?); runtime.depth(context, Value::list(result), &frame, [12, 27])? });
scope.locals.insert("merged".to_string(), { let mut result = RuntimeOrderedMap::new(); for (key, value) in runtime.map_spread(context, &generated_value(&assign.lookup)?, &frame, [42, 51])?.iter() { result.insert(key.clone(), value.clone()); } result.insert(runtime.stringify(context, &Value::text("z"), &frame, [53, 56])?, Value::text("Z")); runtime.depth(context, Value::map(result), &frame, [41, 64])? });
context.write("<section>\u{a}<h1>", &frame, [66, 80])?;
let escaped = runtime.escape(context, &runtime.member(context, &generated_value(&assign.page)?, "title", &frame, [83, 93])?, &frame, [83, 93])?; context.write(&escaped, &frame, [80, 94])?;
context.write("</h1>\u{a}<p class=\"escaped\">", &frame, [94, 119])?;
let escaped = runtime.escape(context, &generated_value(&assign.dangerous)?, &frame, [122, 131])?; context.write(&escaped, &frame, [119, 132])?;
context.write("</p>\u{a}<p class=\"logical\">", &frame, [132, 156])?;
let escaped = runtime.escape(context, &{ let left = generated_value(&assign.flag)?; Value::Bool(if !runtime.truthy(&left) { false } else { runtime.truthy(&Value::text("x")) }) }, &frame, [159, 170])?; context.write(&escaped, &frame, [156, 171])?;
context.write("|", &frame, [171, 172])?;
let escaped = runtime.escape(context, &{ let left = Value::Bool(false); Value::Bool(if runtime.truthy(&left) { true } else { runtime.truthy(&Value::Number(2f64)) }) }, &frame, [175, 185])?; context.write(&escaped, &frame, [172, 186])?;
context.write("</p>\u{a}<p class=\"empty-truthiness\">", &frame, [186, 219])?;
let escaped = runtime.escape(context, &{ let left = generated_value(&assign.empty_list)?; Value::Bool(if !runtime.truthy(&left) { false } else { runtime.truthy(&generated_value(&assign.flag)?) }) }, &frame, [222, 240])?; context.write(&escaped, &frame, [219, 241])?;
context.write("|", &frame, [241, 242])?;
let escaped = runtime.escape(context, &{ let left = generated_value(&assign.empty_map)?; Value::Bool(if !runtime.truthy(&left) { false } else { runtime.truthy(&generated_value(&assign.flag)?) }) }, &frame, [245, 262])?; context.write(&escaped, &frame, [242, 263])?;
context.write("</p>\u{a}<p>", &frame, [263, 271])?;
let escaped = runtime.escape(context, &runtime.index(context, &scope.lookup(&frame, "values"), &Value::Number(1f64), &frame, [274, 283])?, &frame, [274, 283])?; context.write(&escaped, &frame, [271, 284])?;
context.write("|", &frame, [284, 285])?;
let escaped = runtime.escape(context, &runtime.index(context, &scope.lookup(&frame, "merged"), &Value::text("z"), &frame, [288, 299])?, &frame, [288, 299])?; context.write(&escaped, &frame, [285, 300])?;
context.write("</p>\u{a}", &frame, [300, 305])?;
if runtime.truthy(&{ let left = generated_value(&assign.flag)?; Value::Bool(if !runtime.truthy(&left) { false } else { runtime.truthy(&runtime.binary(context, polyspec_template::ast::BinaryOp::Equal, &runtime.member(context, &generated_value(&assign.page)?, "title", &frame, [316, 326])?, &Value::text("Guide"), &frame, [316, 337])?) }) }) {
context.write("<strong>matched</strong>", &frame, [338, 362])?; } else {
context.write("<strong>missed</strong>", &frame, [365, 388])?;
}
context.write("\u{a}<p>", &frame, [391, 395])?;
let escaped = runtime.escape(context, &if runtime.truthy(&generated_value(&assign.flag)?) { Value::text("yes") } else { Value::text("no") }, &frame, [398, 417])?; context.write(&escaped, &frame, [395, 418])?;
context.write("|", &frame, [418, 419])?;
let escaped = runtime.escape(context, &runtime.binary(context, polyspec_template::ast::BinaryOp::Add, &runtime.unary(context, polyspec_template::ast::UnaryOp::Negate, &Value::Number(1f64), &frame, [422, 424])?, &Value::Number(3f64), &frame, [422, 428])?, &frame, [422, 428])?; context.write(&escaped, &frame, [419, 429])?;
context.write("|", &frame, [429, 430])?;
let escaped = runtime.escape(context, &runtime.call(context, "default", vec![Value::text(""), Value::text("fallback")], &frame, [433, 457])?, &frame, [433, 457])?; context.write(&escaped, &frame, [430, 458])?;
context.write("</p>\u{a}<ul>\u{a}", &frame, [458, 468])?;
{ let entries = runtime.entries(context, &generated_value(&assign.rows)?, &frame, [468, 585])?;
let row_size = entries.len();
let row_last_index = entries.len().saturating_sub(1);
let row_previous = scope.locals.get("row").cloned();
for (row_index_raw, (row_key, row_value)) in entries.iter().cloned().enumerate() {
scope.locals.insert("row".to_string(), row_value.clone());
scope.loops.entry("row".to_string()).or_default().push(polyspec_template::render::context::LoopMeta { index: row_index_raw, key: row_key, value: row_value, first: row_index_raw == 0, last: row_index_raw == row_last_index, size: row_size });
context.iterations += 1;
runtime.limit(context, "iteration", context.iterations, &frame, [468, 585])?;
context.write("<li>", &frame, [483, 487])?;
let escaped = runtime.escape(context, &generated_loop_meta(runtime, context, scope, &frame, [490, 500], "row", "index_")?, &frame, [490, 500])?; context.write(&escaped, &frame, [487, 501])?;
context.write("/", &frame, [501, 502])?;
let escaped = runtime.escape(context, &generated_loop_meta(runtime, context, scope, &frame, [505, 514], "row", "size_")?, &frame, [505, 514])?; context.write(&escaped, &frame, [502, 515])?;
context.write(":", &frame, [515, 516])?;
let escaped = runtime.escape(context, &runtime.member(context, &scope.lookup(&frame, "row"), "name", &frame, [519, 527])?, &frame, [519, 527])?; context.write(&escaped, &frame, [516, 528])?;
context.write(":", &frame, [528, 529])?;
let escaped = runtime.escape(context, &generated_loop_meta(runtime, context, scope, &frame, [532, 542], "row", "first_")?, &frame, [532, 542])?; context.write(&escaped, &frame, [529, 543])?;
context.write(":", &frame, [543, 544])?;
let escaped = runtime.escape(context, &generated_loop_meta(runtime, context, scope, &frame, [547, 556], "row", "last_")?, &frame, [547, 556])?; context.write(&escaped, &frame, [544, 557])?;
context.write("</li>\u{a}", &frame, [557, 563])?;
scope.loops.get_mut("row").expect("generated loop stack").pop();
}
if scope.loops.get("row").is_some_and(Vec::is_empty) { scope.loops.remove("row"); }
if let Some(previous) = row_previous { scope.locals.insert("row".to_string(), previous); } else { scope.locals.remove("row"); }
if entries.is_empty() {
context.write("<li>empty</li>\u{a}", &frame, [567, 582])?;
}
}
context.write("</ul>\u{a}", &frame, [586, 592])?;
context.enter("partial.tpl", Some(&frame), Some([592, 607]))?;
let rendered = render_partial_tpl(assign, definitions, InputPartialTpl { values: generated_result::<Vec<f64>>(&scope.lookup(&frame, "values"), "values")? }, context, runtime, root_data, scope);
context.leave();
rendered?;
if definitions.content.is_some() {
context.write("<p>defined</p>", &frame, [620, 634])?;
} else {
context.write("<p>missing</p>", &frame, [637, 651])?;
}
context.write("\u{a}", &frame, [654, 655])?;
{ if definitions.content.is_none() { return Err(runtime.error(context, &frame, [655, 683], ErrorCode::E_RUNTIME_BLOCK_UNDEFINED, "define content is not registered")); }
let definition = definitions.content.clone();
if let Some(html) = definition.as_ref().and_then(|value| value.html.as_ref()) { context.write(html, &frame, [655, 683])?; } else {
let mut input = InputCardTpl::default();
if let Some(data) = definition.as_ref().and_then(|value| value.data.as_ref()) && let Some(value) = data.label.clone() { input.label = value; }
input.label = generated_result::<String>(&runtime.member(context, &generated_value(&assign.page)?, "title", &frame, [672, 682])?, "label")?;
let mut block_scope = Scope::default();
context.enter("card.tpl", Some(&frame), Some([655, 683]))?;
let rendered = render_card_tpl(assign, definitions, input, context, runtime, root_data, &mut block_scope);
context.leave();
rendered?;
}
}
context.write("</section>\u{a}", &frame, [684, 695])?;
Ok(()) }
fn render_partial_tpl(assign: &Assign, definitions: &mut Definitions, input: InputPartialTpl, context: &mut RenderContext<'_>, runtime: &RuntimeBindings, root_data: &Rc<RuntimeOrderedMap>, scope: &mut Scope) -> Result<(), TemplateError> { let _ = (&assign, &definitions, &input, &runtime, &scope); let frame = Frame { name: "partial.tpl".to_string(), lines: Some(LineIndex::from_starts(vec![0, 38])), context: Rc::clone(root_data) };
scope.locals.insert("values".to_string(), generated_value(&input.values)?);
context.write("<p class=\"included\">", &frame, [0, 20])?;
let escaped = runtime.escape(context, &runtime.index(context, &scope.lookup(&frame, "values"), &Value::Number(2f64), &frame, [23, 32])?, &frame, [23, 32])?; context.write(&escaped, &frame, [20, 33])?;
context.write("</p>\u{a}", &frame, [33, 38])?;
Ok(()) }
fn render_template(target: &str, assign: &Assign, definitions: &mut Definitions, context: &mut RenderContext<'_>, runtime: &RuntimeBindings, root_data: &Rc<RuntimeOrderedMap>, scope: &mut Scope) -> Result<(), TemplateError> { match target {
"layout.tpl" => render_layout_tpl(assign, definitions, InputLayoutTpl::default(), context, runtime, root_data, scope),
_ => Err(context.fail(ErrorCode::E_LOAD_NOT_FOUND, None, None, format!("template {target} does not exist"))),
} }
fn generated_error(message: String) -> RequestError { RequestError::Argument(ArgumentError { message }) }
struct GeneratedTarget { target: Option<String>, html: Option<String> }
fn generated_bind_definitions(name: &str, input: &HashMap<String, polyspec_template::DefineInput>) -> Result<(Definitions, HashMap<String, GeneratedTarget>), RequestError> { let mut definitions = Definitions::default(); let mut targets = HashMap::new(); for (id, input) in input { match id.as_str() {
"content" => { if let Some(html) = &input.html { if false || input.template.is_some() || input.data.is_some() { return Err(generated_error(format!("define.{id} has an invalid html entry"))); } definitions.content = Some(Definition { html: Some(html.clone()), ..Default::default() }); targets.insert(id.clone(), GeneratedTarget { target: None, html: Some(html.clone()) }); } else { if input.template.as_deref() != Some("card.tpl") { return Err(generated_error(format!("define.{id} has an invalid template"))); }
let data = match &input.data { None => None, Some(data) => match polyspec_template::bind_data(data).map_err(|error| TemplateError::without_position(error.code, name, error.message))? { Value::Map(map) => { let _ = ↦ Some(DefinitionDataCardTpl { label: map.get("label").map(|value| GeneratedConvert::from_value(value, &format!("define.{id}.data.label"))).transpose().map_err(|error| generated_error(error.message))? }) }, _ => return Err(generated_error(format!("define.{id}.data is not an object"))) } };
definitions.content = Some(Definition { data, ..Default::default() }); targets.insert(id.clone(), GeneratedTarget { target: Some("card.tpl".to_string()), html: None }); } },
"layout" => { if let Some(html) = &input.html { if true || input.template.is_some() || input.data.is_some() { return Err(generated_error(format!("define.{id} has an invalid html entry"))); } definitions.layout = Some(Definition { html: Some(html.clone()), ..Default::default() }); targets.insert(id.clone(), GeneratedTarget { target: None, html: Some(html.clone()) }); } else { if input.template.as_deref() != Some("layout.tpl") { return Err(generated_error(format!("define.{id} has an invalid template"))); }
let data = match &input.data { None => None, Some(data) => match polyspec_template::bind_data(data).map_err(|error| TemplateError::without_position(error.code, name, error.message))? { Value::Map(map) => { let _ = ↦ Some(DefinitionDataLayoutTpl { }) }, _ => return Err(generated_error(format!("define.{id}.data is not an object"))) } };
definitions.layout = Some(Definition { data, ..Default::default() }); targets.insert(id.clone(), GeneratedTarget { target: Some("layout.tpl".to_string()), html: None }); } },
_ => return Err(generated_error(format!("define.{id} is not declared"))),
} } Ok((definitions, targets)) }
pub struct GeneratedProgram { pub runtime: RuntimeEnvironment }
impl GeneratedProgram { pub fn new(runtime: RuntimeEnvironment) -> GeneratedProgram { GeneratedProgram { runtime } } }
impl Program for GeneratedProgram { fn prepare(&self, target: RenderTarget<'_>, assign: &JsonValue, options: &RenderOptions) -> Result<PreparedRender<'_>, RequestError> { let RenderTarget::Name(name) = target else { return Err(generated_error("generated target must be a template name".to_string())); }; internal_boundary(name, || { let root_data = Rc::new(polyspec_template::value::bind::bind_map(assign).map_err(|error| TemplateError::without_position(error.code, name, error.message))?); self.prepare_root(name, root_data, options) }) } fn render(&self, target: RenderTarget<'_>, assign: &JsonValue, options: &RenderOptions) -> Result<String, RequestError> { Ok(self.prepare(target, assign, options)?.render()?) } fn prepare_bound(&self, target: RenderTarget<'_>, assign: &polyspec_template::BoundMap, options: &RenderOptions) -> Result<PreparedRender<'_>, RequestError> { let RenderTarget::Name(name) = target else { return Err(generated_error("generated target must be a template name".to_string())); }; internal_boundary(name, || self.prepare_root(name, polyspec_template::bound_root(assign), options)) } fn render_bound(&self, target: RenderTarget<'_>, assign: &polyspec_template::BoundMap, options: &RenderOptions) -> Result<String, RequestError> { Ok(self.prepare_bound(target, assign, options)?.render()?) } }
impl GeneratedProgram { fn prepare_root<'e>(&'e self, name: &str, root_data: Rc<RuntimeOrderedMap>, options: &RenderOptions) -> Result<PreparedRender<'e>, RequestError> { let typed_assign: Assign = Assign::from_value(&Value::Map(Rc::clone(&root_data)), "assign").map_err(|error| generated_error(error.message))?; let (definitions, targets) = generated_bind_definitions(name, &options.define)?; let resolved = targets.get(name); let target_name = resolved.and_then(|value| value.target.clone()).unwrap_or_else(|| name.to_string()); let html = resolved.and_then(|value| value.html.clone()); let env = options.env.clone().unwrap_or_else(|| polyspec_template::Env { timezone: "Z".to_string(), now: std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|duration| duration.as_secs() as f64).unwrap_or(0.0) }); Ok(PreparedRender::new(name, move || { let mut context = RenderContext::new(&self.runtime, Rc::clone(&root_data), env.clone(), &target_name); let runtime = RuntimeBindings::new(); let mut scope = Scope::default(); let mut render_definitions = definitions.clone(); context.enter(&target_name, None, None)?; let result = if let Some(html) = &html { let frame = Frame { name: target_name.clone(), lines: None, context: Rc::clone(&root_data) }; context.write(html, &frame, [0, 0]) } else { render_template(&target_name, &typed_assign, &mut render_definitions, &mut context, &runtime, &root_data, &mut scope) }; context.leave(); result?; Ok(context.output) })) } }
php · product generated program
{
"schema": 3,
"mode": "gen",
"target": "php",
"entry": "layout.tpl",
"sourceDigest": "936a20a5f3b77bc8a454a40c57b55de1118203b69a39aedda5acb0455f495625",
"typeDigest": "30c0b093d1694670ec7117cc02b36e0031780485e6218be94afbdf1be6524db7",
"contractDigest": "b3a300448cfa0263a9e4fb6888adb359317b6f8e0e9e7f40edc29e8c8671bf2d",
"compilerDigest": "ba7f0bfd4d8024e26cbef4e9ddf0cd1e011489480f00daebb75a11ecfa8e218f",
"files": {
"Page.php": {
"path": "Page.php",
"artifactDigest": "cbffa5fc40d8cf9bc4f774d00647df3d4b3409b6a28c964e2c088cdaf4fa5b47"
},
"Row.php": {
"path": "Row.php",
"artifactDigest": "81c3d16d492639958c7fe3dfd592a98ae751d527785faafdc0a1a98f3a15c80b"
},
"Slot.php": {
"path": "Slot.php",
"artifactDigest": "039e1da3e55bda3debc78b45a39c5c0833b66d4635929b4e965311bb5bf637bc"
},
"Assign.php": {
"path": "Assign.php",
"artifactDigest": "7a46d541770077c3a43366552024d33fafffc44f260007682f528b2bfdb206fd"
},
"InputCardTpl.php": {
"path": "InputCardTpl.php",
"artifactDigest": "99c1740bed0945efc344b5d64f5997ad3d06a2a22891378a5ac753dc5267fa1b"
},
"InputLayoutTpl.php": {
"path": "InputLayoutTpl.php",
"artifactDigest": "90a8c10ab520b9a4b4331b9059a08e8e96a61fa275accbcadd45a2ca636d9647"
},
"InputPartialTpl.php": {
"path": "InputPartialTpl.php",
"artifactDigest": "0dc6c9bd0951fc713a2fdcb139edfb033b5bcfdd068946d8ed28a797750b1d71"
},
"DefinitionDataCardTpl.php": {
"path": "DefinitionDataCardTpl.php",
"artifactDigest": "571578dcca2072e25c17010c571b3a06d79698ddfbc1fb584d66b1dbc2d105a8"
},
"DefinitionDataLayoutTpl.php": {
"path": "DefinitionDataLayoutTpl.php",
"artifactDigest": "3678921062097582186777e14817310de4126a2c27212fc0f4119dc802d4fcab"
},
"DefinitionDataPartialTpl.php": {
"path": "DefinitionDataPartialTpl.php",
"artifactDigest": "e7189ec40151b92aeef12855c79edf8d4f8b25e3360156fc83fe9635f4f23dff"
},
"Definition.php": {
"path": "Definition.php",
"artifactDigest": "329d06a2941db3c464c437163ad1e21ab20b10533dafb463ae1febff303a22fa"
},
"Definitions.php": {
"path": "Definitions.php",
"artifactDigest": "2a01e1bd496f4da05fb3a864650e1378601c1db61380d76eb39161fd3fb58c20"
},
"ArtifactManifest.php": {
"path": "ArtifactManifest.php",
"artifactDigest": "ef8947a09ffc0b7fccd351f03e499115649587919a224b008af92d17ddeff03a"
},
"GeneratedRuntime.php": {
"path": "GeneratedRuntime.php",
"artifactDigest": "f47c6e4c65c0c9968475b5f8212649b3ffe52c1a1b6df1bd6403248c6a257d3b"
},
"GeneratedTemplates.php": {
"path": "GeneratedTemplates.php",
"artifactDigest": "8cef0a6b9e6140dd4fd3cd1b419910af458674c7f81bd3988b41923512464dd2"
},
"GeneratedExecution.php": {
"path": "GeneratedExecution.php",
"artifactDigest": "ba79633119f4765a2ea980fd34f53bfa8de88108ce74cf48864eb21409de290b"
},
"GeneratedProgram.php": {
"path": "GeneratedProgram.php",
"artifactDigest": "cc53ad4abb1dc4503fe6a743efb927db39ab6ab608f6abbcc392b84c11462d90"
}
}
}
<?php
declare(strict_types=1);
namespace Polyspec\Showcase\Generated\CompilerCoverage;
use Polyspec\Template\Render\Context;
use Polyspec\Template\Render\Frame;
use Polyspec\Template\Render\RuntimeBindings;
use Polyspec\Template\Render\Scope;
use Polyspec\Template\Value\MapValue;
final class GeneratedTemplates
{
public static function render(
string $target,
Assign $assign,
Definitions $definitions,
Context $context,
RuntimeBindings $runtime,
MapValue $rootData,
Scope $scope,
): void {
switch ($target) {
case 'layout.tpl':
self::renderLayoutTpl(
$assign,
$definitions,
new InputLayoutTpl(),
$context,
$runtime,
$rootData,
$scope,
);
return;
default:
throw $context->fail('E_LOAD_NOT_FOUND', null, null, 'template ' . $target . ' does not exist');
}
}
private static function renderCardTpl(
Assign $assign,
Definitions $definitions,
InputCardTpl $input,
Context $context,
RuntimeBindings $runtime,
MapValue $rootData,
Scope $scope,
): void {
$frame = new Frame('card.tpl', [0, 30], $rootData);
$scope->locals->set('label', $input->label);
$context->at($frame, [0, 16]);
$context->write('<p class="card">');
$context->at($frame, [16, 25]);
$context->write($runtime->escape($scope->lookup($frame, 'label'), $frame, [19, 24]));
$context->at($frame, [25, 30]);
$context->write(('</p>' . "\n"));
}
private static function renderLayoutTpl(
Assign $assign,
Definitions $definitions,
InputLayoutTpl $input,
Context $context,
RuntimeBindings $runtime,
MapValue $rootData,
Scope $scope,
): void {
$frame = new Frame(
'layout.tpl',
[0, 29, 66, 76, 100, 137, 191, 268, 305, 392, 463, 468, 483, 563, 567, 582, 586, 592, 608, 655, 684, 695],
$rootData,
);
$scope->locals->set(
'values',
$runtime->depth(
GeneratedRuntime::list(
[
['spread' => false, 'value' => 0],
['spread' => true, 'value' => $runtime->listSpread($assign->numbers, $frame, [16, 26])],
],
),
$frame,
[12, 27],
),
);
$scope->locals->set(
'merged',
$runtime->depth(
GeneratedRuntime::map(
[
['spread' => true, 'value' => $runtime->mapSpread($assign->lookup, $frame, [42, 51])],
['spread' => false, 'key' => 'z', 'value' => 'Z'],
],
),
$frame,
[41, 64],
),
);
$context->at($frame, [66, 80]);
$context->write(('<section>' . "\n" . '<h1>'));
$context->at($frame, [80, 94]);
$context->write($runtime->escape($assign->page?->title, $frame, [83, 93]));
$context->at($frame, [94, 119]);
$context->write(('</h1>' . "\n" . '<p class="escaped">'));
$context->at($frame, [119, 132]);
$context->write($runtime->escape($assign->dangerous, $frame, [122, 131]));
$context->at($frame, [132, 156]);
$context->write(('</p>' . "\n" . '<p class="logical">'));
$context->at($frame, [156, 171]);
$context->write(
$runtime->escape(($runtime->truthy($assign->flag) && $runtime->truthy('x')), $frame, [159, 170]),
);
$context->at($frame, [171, 172]);
$context->write('|');
$context->at($frame, [172, 186]);
$context->write($runtime->escape(($runtime->truthy(false) || $runtime->truthy(2)), $frame, [175, 185]));
$context->at($frame, [186, 219]);
$context->write(('</p>' . "\n" . '<p class="empty-truthiness">'));
$context->at($frame, [219, 241]);
$context->write(
$runtime->escape(
($runtime->truthy($assign->empty_list) && $runtime->truthy($assign->flag)),
$frame,
[222, 240],
),
);
$context->at($frame, [241, 242]);
$context->write('|');
$context->at($frame, [242, 263]);
$context->write(
$runtime->escape(
($runtime->truthy($assign->empty_map) && $runtime->truthy($assign->flag)),
$frame,
[245, 262],
),
);
$context->at($frame, [263, 271]);
$context->write(('</p>' . "\n" . '<p>'));
$context->at($frame, [271, 284]);
$context->write(
$runtime->escape(
$runtime->index($scope->lookup($frame, 'values'), 1, $frame, [274, 283]),
$frame,
[274, 283],
),
);
$context->at($frame, [284, 285]);
$context->write('|');
$context->at($frame, [285, 300]);
$context->write(
$runtime->escape(
$runtime->index($scope->lookup($frame, 'merged'), 'z', $frame, [288, 299]),
$frame,
[288, 299],
),
);
$context->at($frame, [300, 305]);
$context->write(('</p>' . "\n"));
if (
$runtime->truthy(
($runtime->truthy($assign->flag)
&& $runtime->truthy($runtime->binary('==', $assign->page?->title, 'Guide', $frame, [316, 337]))),
)
) {
$context->at($frame, [338, 362]);
$context->write('<strong>matched</strong>');
} else {
$context->at($frame, [365, 388]);
$context->write('<strong>missed</strong>');
}
$context->at($frame, [391, 395]);
$context->write(("\n" . '<p>'));
$context->at($frame, [395, 418]);
$context->write($runtime->escape(($runtime->truthy($assign->flag) ? 'yes' : 'no'), $frame, [398, 417]));
$context->at($frame, [418, 419]);
$context->write('|');
$context->at($frame, [419, 429]);
$context->write(
$runtime->escape(
$runtime->binary('+', $runtime->unary('-', 1, $frame, [422, 424]), 3, $frame, [422, 428]),
$frame,
[422, 428],
),
);
$context->at($frame, [429, 430]);
$context->write('|');
$context->at($frame, [430, 458]);
$context->write(
$runtime->escape($runtime->call('default', ['', 'fallback'], $frame, [433, 457]), $frame, [433, 457]),
);
$context->at($frame, [458, 468]);
$context->write(('</p>' . "\n" . '<ul>' . "\n"));
$row_entries = $runtime->entries($assign->rows, $frame, [468, 585]);
$row_size = count($row_entries);
$row_last_index = $row_size - 1;
$row_had = $scope->locals->has('row');
$row_previous = $scope->locals->get('row');
foreach ($row_entries as $row_index => [$row_key, $row_value]) {
$scope->locals->set('row', $row_value);
$row_stack = $scope->loops['row'] ?? [];
$row_stack[] = [
'index_' => (float) $row_index,
'size_' => (float) $row_size,
'first_' => $row_index === 0,
'last_' => $row_index === $row_last_index,
'key_' => $row_key,
'value_' => $row_value,
];
$scope->loops['row'] = $row_stack;
$context->iterations++;
$runtime->limit('iteration', $context->iterations, $frame, [468, 585]);
$context->at($frame, [483, 487]);
$context->write('<li>');
$context->at($frame, [487, 501]);
$context->write($runtime->escape($scope->loopMeta('row')['index_'], $frame, [490, 500]));
$context->at($frame, [501, 502]);
$context->write('/');
$context->at($frame, [502, 515]);
$context->write($runtime->escape($scope->loopMeta('row')['size_'], $frame, [505, 514]));
$context->at($frame, [515, 516]);
$context->write(':');
$context->at($frame, [516, 528]);
$context->write($runtime->escape($scope->lookup($frame, 'row')?->name, $frame, [519, 527]));
$context->at($frame, [528, 529]);
$context->write(':');
$context->at($frame, [529, 543]);
$context->write($runtime->escape($scope->loopMeta('row')['first_'], $frame, [532, 542]));
$context->at($frame, [543, 544]);
$context->write(':');
$context->at($frame, [544, 557]);
$context->write($runtime->escape($scope->loopMeta('row')['last_'], $frame, [547, 556]));
$context->at($frame, [557, 563]);
$context->write(('</li>' . "\n"));
$row_stack = $scope->loops['row'];
array_pop($row_stack);
if ($row_stack === []) {
unset($scope->loops['row']);
} else {
$scope->loops['row'] = $row_stack;
}
}
if ($row_had) {
$scope->locals->set('row', $row_previous);
} else {
$scope->locals->remove('row');
}
if ($row_size === 0) {
$context->at($frame, [567, 582]);
$context->write(('<li>empty</li>' . "\n"));
}
$context->at($frame, [586, 592]);
$context->write(('</ul>' . "\n"));
$context->enter('partial.tpl', $frame, [592, 607]);
try {
self::renderPartialTpl(
$assign,
$definitions,
new InputPartialTpl(values: $scope->lookup($frame, 'values')),
$context,
$runtime,
$rootData,
$scope,
);
} finally {
$context->leave();
}
if ($definitions->content !== null) {
$context->at($frame, [620, 634]);
$context->write('<p>defined</p>');
} else {
$context->at($frame, [637, 651]);
$context->write('<p>missing</p>');
}
$context->at($frame, [654, 655]);
$context->write("\n");
$definition = $definitions->content;
if ($definition === null) {
throw $runtime->error($frame, [655, 683], 'E_RUNTIME_BLOCK_UNDEFINED', 'define content is not registered');
}
if ($definition?->html !== null) {
$context->at($frame, [655, 683]);
$context->write($definition->html);
} else {
if ($definition?->data !== null && !($definition->data instanceof DefinitionDataCardTpl)) {
throw $runtime->error(
$frame,
[655, 683],
'E_RUNTIME_TYPE',
'generated definition content data has an invalid type',
);
}
$scoped_label = $assign->page?->title;
$input = new InputCardTpl(label: $scoped_label);
if ($definition?->data !== null) {
if ($definition->data->has_label) {
$input->label = $definition->data->label;
}
}
$input->label = $scoped_label;
$blockScope = new Scope();
$context->enter('card.tpl', $frame, [655, 683]);
try {
self::renderCardTpl($assign, $definitions, $input, $context, $runtime, $rootData, $blockScope);
} finally {
$context->leave();
}
}
$context->at($frame, [684, 695]);
$context->write(('</section>' . "\n"));
}
private static function renderPartialTpl(
Assign $assign,
Definitions $definitions,
InputPartialTpl $input,
Context $context,
RuntimeBindings $runtime,
MapValue $rootData,
Scope $scope,
): void {
$frame = new Frame('partial.tpl', [0, 38], $rootData);
$scope->locals->set('values', $input->values);
$context->at($frame, [0, 20]);
$context->write('<p class="included">');
$context->at($frame, [20, 33]);
$context->write(
$runtime->escape($runtime->index($scope->lookup($frame, 'values'), 2, $frame, [23, 32]), $frame, [23, 32]),
);
$context->at($frame, [33, 38]);
$context->write(('</p>' . "\n"));
}
}
Output · 351 bytes · f9e5af2122de…
<section> <h1>Guide</h1> <p class="escaped">&<>"'</p> <p class="logical">true|true</p> <p class="empty-truthiness">false|false</p> <p>10|Z</p> <strong>matched</strong> <p>yes|2|fallback</p> <ul> <li>0/2:A:true:false</li> <li>1/2:B:false:true</li> </ul> <p class="included">20</p> <p>defined</p> <p class="card">Guide</p> </section>
| Implementation | ts | go | rust | php | python |
|---|---|---|---|---|---|
| artifact render | PASS | PASS | PASS | PASS | PASS |