@json-schema-engine/compiler 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/emit.d.ts +52 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +166 -0
- package/dist/emit.js.map +1 -0
- package/dist/explain.d.ts +28 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +42 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +132 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/dist/index.js.map +1 -0
- package/dist/plan.d.ts +83 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +385 -0
- package/dist/plan.js.map +1 -0
- package/dist/runtime-compile.d.ts +30 -0
- package/dist/runtime-compile.d.ts.map +1 -0
- package/dist/runtime-compile.js +29 -0
- package/dist/runtime-compile.js.map +1 -0
- package/dist/runtime.d.ts +157 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +336 -0
- package/dist/runtime.js.map +1 -0
- package/dist/serialize/apply.d.ts +28 -0
- package/dist/serialize/apply.d.ts.map +1 -0
- package/dist/serialize/apply.js +211 -0
- package/dist/serialize/apply.js.map +1 -0
- package/dist/serialize/context.d.ts +143 -0
- package/dist/serialize/context.d.ts.map +1 -0
- package/dist/serialize/context.js +93 -0
- package/dist/serialize/context.js.map +1 -0
- package/dist/serialize/expressions.d.ts +7 -0
- package/dist/serialize/expressions.d.ts.map +1 -0
- package/dist/serialize/expressions.js +177 -0
- package/dist/serialize/expressions.js.map +1 -0
- package/dist/serialize/guards.d.ts +7 -0
- package/dist/serialize/guards.d.ts.map +1 -0
- package/dist/serialize/guards.js +19 -0
- package/dist/serialize/guards.js.map +1 -0
- package/dist/serialize/index.d.ts +7 -0
- package/dist/serialize/index.d.ts.map +1 -0
- package/dist/serialize/index.js +266 -0
- package/dist/serialize/index.js.map +1 -0
- package/dist/serialize/keywords.d.ts +32 -0
- package/dist/serialize/keywords.d.ts.map +1 -0
- package/dist/serialize/keywords.js +131 -0
- package/dist/serialize/keywords.js.map +1 -0
- package/dist/serialize/messages.d.ts +36 -0
- package/dist/serialize/messages.d.ts.map +1 -0
- package/dist/serialize/messages.js +100 -0
- package/dist/serialize/messages.js.map +1 -0
- package/dist/serialize/names.d.ts +47 -0
- package/dist/serialize/names.d.ts.map +1 -0
- package/dist/serialize/names.js +115 -0
- package/dist/serialize/names.js.map +1 -0
- package/dist/serialize/spans.d.ts +34 -0
- package/dist/serialize/spans.d.ts.map +1 -0
- package/dist/serialize/spans.js +61 -0
- package/dist/serialize/spans.js.map +1 -0
- package/dist/serialize/statements.d.ts +16 -0
- package/dist/serialize/statements.d.ts.map +1 -0
- package/dist/serialize/statements.js +396 -0
- package/dist/serialize/statements.js.map +1 -0
- package/dist/serialize/unit.d.ts +8 -0
- package/dist/serialize/unit.d.ts.map +1 -0
- package/dist/serialize/unit.js +107 -0
- package/dist/serialize/unit.js.map +1 -0
- package/dist/standalone.d.ts +24 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +166 -0
- package/dist/standalone.js.map +1 -0
- package/package.json +38 -0
- package/src/emit.ts +196 -0
- package/src/explain.ts +67 -0
- package/src/index.ts +366 -0
- package/src/plan.ts +496 -0
- package/src/runtime-compile.ts +90 -0
- package/src/runtime.ts +599 -0
- package/src/serialize/apply.ts +251 -0
- package/src/serialize/context.ts +161 -0
- package/src/serialize/expressions.ts +202 -0
- package/src/serialize/guards.ts +20 -0
- package/src/serialize/index.ts +432 -0
- package/src/serialize/keywords.ts +144 -0
- package/src/serialize/messages.ts +120 -0
- package/src/serialize/names.ts +136 -0
- package/src/serialize/spans.ts +88 -0
- package/src/serialize/statements.ts +531 -0
- package/src/serialize/unit.ts +118 -0
- package/src/standalone.ts +187 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Emitted identifier names and IR scans shared by the serializer's modules.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type LowerProduceValue,
|
|
5
|
+
type LowerStmt,
|
|
6
|
+
} from "@json-schema-engine/core";
|
|
7
|
+
import { type CodeChunk, id } from "../emit.js";
|
|
8
|
+
import type { EmitOutput, UnitContext } from "./context.js";
|
|
9
|
+
|
|
10
|
+
export const V = id("v"); // instance parameter
|
|
11
|
+
export const D = id("d"); // depth parameter
|
|
12
|
+
export const S = id("s"); // dynamic-scope parameter
|
|
13
|
+
export const R = id("R"); // runtime closure
|
|
14
|
+
export const T = id("T"); // interpreted-target table
|
|
15
|
+
export const EV = id("ev"); // runtime coverage channel (region emission, phase B)
|
|
16
|
+
export const EP = id("ep"); // evaluation-path prefix parameter (list emission)
|
|
17
|
+
export const IP = id("ip"); // instance-pointer prefix parameter (list emission)
|
|
18
|
+
export const ERRS = id("errs"); // error channel (list emission)
|
|
19
|
+
export const ANNS = id("anns"); // annotation channel (annotation mode)
|
|
20
|
+
export const ST = id("st"); // per-evaluation trace state (trace emission)
|
|
21
|
+
export const TP = id("tp"); // parent application node parameter (trace emission)
|
|
22
|
+
export const TN = id("tn"); // this application's node (trace emission)
|
|
23
|
+
|
|
24
|
+
/** Which channels an emission threads through every application. */
|
|
25
|
+
export interface ChannelShape {
|
|
26
|
+
output: EmitOutput;
|
|
27
|
+
annMode: boolean;
|
|
28
|
+
trace: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const shapeOf = (ctx: UnitContext): ChannelShape => ({
|
|
32
|
+
output: ctx.output,
|
|
33
|
+
annMode: ctx.annMode,
|
|
34
|
+
trace: ctx.trace,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The arguments every application carries after the instance, depth, and
|
|
39
|
+
* scope: in list emission the two location prefixes and the record
|
|
40
|
+
* channels (the trace state and parent node under trace emission), and the
|
|
41
|
+
* coverage channel for an in-place region call. Unit signatures, unit
|
|
42
|
+
* calls, trampolines, and the root call all build from here, so the
|
|
43
|
+
* variants cannot drift apart. `locate` is called only in list emission —
|
|
44
|
+
* a flag-mode apply never computes its prefixes.
|
|
45
|
+
*/
|
|
46
|
+
export function channelArgs(
|
|
47
|
+
shape: ChannelShape,
|
|
48
|
+
locate: () => [CodeChunk, CodeChunk],
|
|
49
|
+
parent: CodeChunk,
|
|
50
|
+
ev: boolean,
|
|
51
|
+
): CodeChunk[] {
|
|
52
|
+
const args: CodeChunk[] = [];
|
|
53
|
+
if (shape.output === "list") {
|
|
54
|
+
args.push(...locate());
|
|
55
|
+
if (shape.trace) args.push(ST, parent);
|
|
56
|
+
else {
|
|
57
|
+
args.push(ERRS);
|
|
58
|
+
if (shape.annMode) args.push(ANNS);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (ev) args.push(EV);
|
|
62
|
+
return args;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The interpreter trampoline an emission calls for an interpreted target. */
|
|
66
|
+
export function fragHelper(
|
|
67
|
+
shape: ChannelShape,
|
|
68
|
+
inPlaceRegion: boolean,
|
|
69
|
+
): CodeChunk {
|
|
70
|
+
if (shape.output !== "list") return id(inPlaceRegion ? "h_fragc" : "h_frag");
|
|
71
|
+
if (shape.trace) return id("h_fragt");
|
|
72
|
+
return id(shape.annMode ? "h_fragla" : "h_fragl");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const unitFn = (index: number): CodeChunk => id("u" + String(index));
|
|
76
|
+
// Region-variant of a unit function: same body with the trailing coverage
|
|
77
|
+
// channel threaded (COMPILED-CONSUMERS.md phase B). A distinct name so a unit
|
|
78
|
+
// can carry both a plain and a channel-threaded emission.
|
|
79
|
+
export const unitFnRegion = (index: number): CodeChunk =>
|
|
80
|
+
id("u" + String(index) + "c");
|
|
81
|
+
export const bindingVar = (n: number): CodeChunk => id("b" + String(n));
|
|
82
|
+
export const regexConst = (i: number): CodeChunk => id("r" + String(i));
|
|
83
|
+
export const formatConst = (i: number): CodeChunk => id("fmt" + String(i));
|
|
84
|
+
export const counterVar = (n: number): CodeChunk => id("c" + String(n));
|
|
85
|
+
export const foldVar = (n: number): CodeChunk => id("f" + String(n));
|
|
86
|
+
|
|
87
|
+
/** First `produce` node in a keyword's lowered statement list (searched into blocks). */
|
|
88
|
+
export function findProduce(
|
|
89
|
+
stmts: readonly LowerStmt[],
|
|
90
|
+
): LowerProduceValue | null {
|
|
91
|
+
for (const stmt of stmts) {
|
|
92
|
+
switch (stmt.kind) {
|
|
93
|
+
case "produce":
|
|
94
|
+
return stmt.value;
|
|
95
|
+
case "if": {
|
|
96
|
+
const t = findProduce(stmt.then);
|
|
97
|
+
if (t) return t;
|
|
98
|
+
if (stmt.else) {
|
|
99
|
+
const e = findProduce(stmt.else);
|
|
100
|
+
if (e) return e;
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "forEachKey":
|
|
105
|
+
case "forEachIndex": {
|
|
106
|
+
const b = findProduce(stmt.body);
|
|
107
|
+
if (b) return b;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** True when a keyword's lowered statement list contains an `annotate` node (searched into blocks). */
|
|
118
|
+
export function hasAnnotate(stmts: readonly LowerStmt[]): boolean {
|
|
119
|
+
for (const stmt of stmts) {
|
|
120
|
+
switch (stmt.kind) {
|
|
121
|
+
case "annotate":
|
|
122
|
+
return true;
|
|
123
|
+
case "if":
|
|
124
|
+
if (hasAnnotate(stmt.then) || (stmt.else && hasAnnotate(stmt.else)))
|
|
125
|
+
return true;
|
|
126
|
+
break;
|
|
127
|
+
case "forEachKey":
|
|
128
|
+
case "forEachIndex":
|
|
129
|
+
if (hasAnnotate(stmt.body)) return true;
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Channel spans at application boundaries: the annotation and coverage
|
|
2
|
+
// channels mark before an application and truncate when it fails (rule 3).
|
|
3
|
+
|
|
4
|
+
import { type CodeChunk, id, join, js } from "../emit.js";
|
|
5
|
+
import { ANNS, EV, ST, TN } from "./names.js";
|
|
6
|
+
import { UnitContext } from "./context.js";
|
|
7
|
+
|
|
8
|
+
/** One marked channel: the annotation channel or the coverage channel. */
|
|
9
|
+
export interface Span {
|
|
10
|
+
kind: "anns" | "ev";
|
|
11
|
+
m: CodeChunk;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The annotation channel's current length: the value a span mark takes. */
|
|
15
|
+
export function annsLength(ctx: UnitContext): CodeChunk {
|
|
16
|
+
return ctx.trace ? js`${ST}.anns.length` : js`${ANNS}.length`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Drop the annotations pushed since mark `m` (a failed application's).
|
|
21
|
+
* Trace emission routes the cut through the runtime, which discards or
|
|
22
|
+
* retains them by the artifact's level.
|
|
23
|
+
*/
|
|
24
|
+
export function annsCut(ctx: UnitContext, m: CodeChunk): CodeChunk {
|
|
25
|
+
return ctx.trace
|
|
26
|
+
? js`${id("h_cutA")}(${ST}, ${m});`
|
|
27
|
+
: js`${ANNS}.length = ${m};`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Push one annotation unit; trace emission records the application with it. */
|
|
31
|
+
export function annsPush(ctx: UnitContext, unit: CodeChunk): CodeChunk {
|
|
32
|
+
return ctx.trace
|
|
33
|
+
? js`${id("h_ann")}(${ST}, ${TN}, ${unit});`
|
|
34
|
+
: js`${ANNS}.push(${unit});`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The channel spans active at an application boundary: `anns` in annotation
|
|
39
|
+
* mode, `ev` in region emission, both when composed, none otherwise. errs
|
|
40
|
+
* is not one of them: a failed application keeps its errors (they become
|
|
41
|
+
* irrelevant only when an enclosing KEYWORD accepts, draft-03 §12.2), so
|
|
42
|
+
* error truncation is keyword-scoped — see errMark() — not per branch.
|
|
43
|
+
*/
|
|
44
|
+
export function channelSpans(ctx: UnitContext, includeEv = true): Span[] {
|
|
45
|
+
const spans: Span[] = [];
|
|
46
|
+
if (ctx.annMode) {
|
|
47
|
+
spans.push({ kind: "anns", m: id("m" + String(ctx.counters.temp++)) });
|
|
48
|
+
}
|
|
49
|
+
if (ctx.regionMode && includeEv) {
|
|
50
|
+
spans.push({ kind: "ev", m: id("m" + String(ctx.counters.temp++)) });
|
|
51
|
+
}
|
|
52
|
+
return spans;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function spanDecls(ctx: UnitContext, spans: Span[]): CodeChunk {
|
|
56
|
+
return join(
|
|
57
|
+
" ",
|
|
58
|
+
spans.map(({ kind, m }) =>
|
|
59
|
+
kind === "anns"
|
|
60
|
+
? js`const ${m} = ${annsLength(ctx)};`
|
|
61
|
+
: js`const ${m} = ${EV}.length;`,
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function spanResets(ctx: UnitContext, spans: Span[]): CodeChunk {
|
|
67
|
+
return join(
|
|
68
|
+
" ",
|
|
69
|
+
spans.map(({ kind, m }) =>
|
|
70
|
+
kind === "anns" ? annsCut(ctx, m) : js`${EV}.length = ${m};`,
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* One grouped-fold branch: run `call`, apply `hit` on success, truncate the
|
|
77
|
+
* active channel spans on failure (a failed branch's records and
|
|
78
|
+
* coverage discard; a passing branch's merge — channel rule 3 per branch).
|
|
79
|
+
*/
|
|
80
|
+
export function branchSpan(
|
|
81
|
+
ctx: UnitContext,
|
|
82
|
+
call: CodeChunk,
|
|
83
|
+
hit: CodeChunk,
|
|
84
|
+
): CodeChunk {
|
|
85
|
+
const spans = channelSpans(ctx);
|
|
86
|
+
if (spans.length === 0) return js`if (${call}) { ${hit} }`;
|
|
87
|
+
return js`{ ${spanDecls(ctx, spans)} if (${call}) { ${hit} } else { ${spanResets(ctx, spans)} } }`;
|
|
88
|
+
}
|