@mica-js/device-model 0.0.0-stage → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3433 @@
1
+ //#region src/errors.ts
2
+ function snapshot(input, ancestors = /* @__PURE__ */ new Set(), path = []) {
3
+ if (input === null || typeof input !== "object") {
4
+ if (typeof input === "function" || typeof input === "symbol" || typeof input === "bigint") fail("IR_SCHEMA", "Expected ordinary IR data.", path);
5
+ return input;
6
+ }
7
+ if (ancestors.has(input)) fail("IR_SCHEMA", "Cyclic IR data is not supported.", path);
8
+ ancestors.add(input);
9
+ let output;
10
+ if (input instanceof Uint8Array) output = Object.freeze(Array.from(Uint8Array.prototype.values.call(input)));
11
+ else if (Array.isArray(input)) {
12
+ if (Object.getPrototypeOf(input) !== Array.prototype) fail("IR_SCHEMA", "Expected an ordinary IR array.");
13
+ const result = [];
14
+ for (const key of Reflect.ownKeys(input)) {
15
+ if (key === "length") continue;
16
+ if (typeof key !== "string" || !/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= input.length) fail("IR_SCHEMA", "IR arrays cannot contain extra properties.");
17
+ if (!("value" in Object.getOwnPropertyDescriptor(input, key))) fail("IR_SCHEMA", "IR arrays cannot contain accessors.");
18
+ }
19
+ for (let i = 0; i < input.length; i++) {
20
+ if (!Object.hasOwn(input, i)) fail("IR_SCHEMA", "IR arrays cannot be sparse.");
21
+ result.push(snapshot(Object.getOwnPropertyDescriptor(input, i).value, ancestors, [...path, i]));
22
+ }
23
+ output = Object.freeze(result);
24
+ } else {
25
+ const prototype = Object.getPrototypeOf(input);
26
+ if (prototype !== Object.prototype && prototype !== null) fail("IR_SCHEMA", "Expected an ordinary data object.");
27
+ const result = {};
28
+ for (const key of Reflect.ownKeys(input)) {
29
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
30
+ if (typeof key !== "string" || !("value" in descriptor)) fail("IR_SCHEMA", "IR data cannot contain symbols or accessors.");
31
+ Object.defineProperty(result, key, {
32
+ value: snapshot(descriptor.value, ancestors, [...path, key]),
33
+ enumerable: true
34
+ });
35
+ }
36
+ output = Object.freeze(result);
37
+ }
38
+ ancestors.delete(input);
39
+ return output;
40
+ }
41
+ var BuildError = class extends Error {
42
+ diagnostics;
43
+ constructor(diagnostics) {
44
+ if (!diagnostics.length) throw new TypeError("BuildError requires at least one diagnostic.");
45
+ super(diagnostics.map((diagnostic) => diagnostic.message).join("\n"));
46
+ this.name = "BuildError";
47
+ this.diagnostics = snapshot(diagnostics);
48
+ }
49
+ };
50
+ function fail(code, message, path = [], phase = "finalize", source) {
51
+ throw new BuildError([{
52
+ code,
53
+ message,
54
+ path,
55
+ phase,
56
+ related: [],
57
+ ...source === void 0 ? {} : { source }
58
+ }]);
59
+ }
60
+ //#endregion
61
+ //#region src/schema.ts
62
+ var builtins = Object.freeze({
63
+ Logic: [
64
+ "low",
65
+ "high",
66
+ "unknown"
67
+ ],
68
+ SampleStatus: ["valid", "floating"],
69
+ Overflow: [
70
+ "drop_new",
71
+ "drop_oldest",
72
+ "error"
73
+ ]
74
+ });
75
+ var unary = [
76
+ "positive",
77
+ "negate",
78
+ "not",
79
+ "bitNot"
80
+ ];
81
+ var binary = [
82
+ "add",
83
+ "subtract",
84
+ "multiply",
85
+ "divide",
86
+ "remainder",
87
+ "power",
88
+ "lessThan",
89
+ "lessThanOrEqual",
90
+ "greaterThan",
91
+ "greaterThanOrEqual",
92
+ "equal",
93
+ "notEqual",
94
+ "concat",
95
+ "bitAnd",
96
+ "bitOr",
97
+ "bitXor",
98
+ "shiftLeft",
99
+ "shiftRight",
100
+ "shiftRightUnsigned"
101
+ ];
102
+ var numeric = [
103
+ "isNaN",
104
+ "isFinite",
105
+ "isInteger",
106
+ "isSafeInteger",
107
+ "abs",
108
+ "floor",
109
+ "ceil",
110
+ "trunc",
111
+ "round",
112
+ "sqrt",
113
+ "min",
114
+ "max",
115
+ "pow"
116
+ ];
117
+ var loads = [
118
+ "load8",
119
+ "load16le",
120
+ "load16be",
121
+ "load32le",
122
+ "load32be"
123
+ ];
124
+ var stores = [
125
+ "store8",
126
+ "store16le",
127
+ "store16be",
128
+ "store32le",
129
+ "store32be"
130
+ ];
131
+ var valueKinds = [
132
+ "number",
133
+ "bool",
134
+ "string",
135
+ "bytes",
136
+ "sample",
137
+ "intent",
138
+ "enum",
139
+ "record"
140
+ ];
141
+ function object(value, path) {
142
+ if (value === null || typeof value !== "object" || Array.isArray(value)) fail("IR_SCHEMA", "Expected a data object.", path);
143
+ }
144
+ function fields(n, required, optional, p, located = true) {
145
+ for (const key of required) if (!Object.hasOwn(n, key)) fail("IR_SCHEMA", `Missing field ${key}.`, [...p, key]);
146
+ for (const key of Object.keys(n)) if (![
147
+ ...required,
148
+ ...optional,
149
+ ...located ? ["source"] : []
150
+ ].includes(key)) fail("IR_SCHEMA", `Unexpected field ${key}.`, [...p, key]);
151
+ if (Object.hasOwn(n, "source")) source(n.source, [...p, "source"]);
152
+ }
153
+ function text(v, p) {
154
+ if (typeof v !== "string") fail("IR_SCHEMA", "Expected a string.", p);
155
+ }
156
+ function bool(v, p) {
157
+ if (typeof v !== "boolean") fail("IR_SCHEMA", "Expected a boolean.", p);
158
+ }
159
+ function num(v, p) {
160
+ if (typeof v !== "number") fail("IR_SCHEMA", "Expected a number.", p);
161
+ }
162
+ function id(v, p) {
163
+ if (typeof v !== "number" || !Number.isSafeInteger(v) || v < 0) fail("IR_SCHEMA", "Expected a nonnegative safe integer.", p);
164
+ }
165
+ function choice(v, choices, p) {
166
+ if (!choices.includes(v)) fail("IR_SCHEMA", `Expected one of ${choices.join(", ")}.`, p);
167
+ }
168
+ function list(v, p, visit) {
169
+ if (!Array.isArray(v)) fail("IR_SCHEMA", "Expected an array.", p);
170
+ for (let i = 0; i < v.length; i++) visit(v[i], [...p, i]);
171
+ }
172
+ function source(v, p) {
173
+ object(v, p);
174
+ const hasUri = Object.hasOwn(v, "uri");
175
+ for (const key of Object.keys(v)) if (![
176
+ "uri",
177
+ "range",
178
+ "label"
179
+ ].includes(key)) fail("IR_SCHEMA", "Invalid source field.", [...p, key]);
180
+ if (Object.hasOwn(v, "label")) text(v.label, [...p, "label"]);
181
+ if (!hasUri) {
182
+ if (!Object.hasOwn(v, "label") || Object.hasOwn(v, "range")) fail("IR_SCHEMA", "Source requires a label or URI and range.", p);
183
+ return;
184
+ }
185
+ text(v.uri, [...p, "uri"]);
186
+ object(v.range, [...p, "range"]);
187
+ const r = v.range;
188
+ for (const key of Object.keys(r)) if (!["start", "end"].includes(key)) fail("IR_SCHEMA", "Invalid range field.", [
189
+ ...p,
190
+ "range",
191
+ key
192
+ ]);
193
+ for (const key of ["start", "end"]) {
194
+ const position = r[key];
195
+ object(position, [
196
+ ...p,
197
+ "range",
198
+ key
199
+ ]);
200
+ for (const k of Object.keys(position)) if (!["line", "column"].includes(k)) fail("IR_SCHEMA", "Invalid position field.", [
201
+ ...p,
202
+ "range",
203
+ key,
204
+ k
205
+ ]);
206
+ for (const k of ["line", "column"]) if (!Number.isSafeInteger(position[k]) || position[k] < 1) fail("IR_SCHEMA", "Positions start at one.", [
207
+ ...p,
208
+ "range",
209
+ key,
210
+ k
211
+ ]);
212
+ }
213
+ const { start, end } = r;
214
+ if (end.line < start.line || end.line === start.line && end.column < start.column) fail("IR_SCHEMA", "Source range is reversed.", p);
215
+ }
216
+ function valueType(v, p, optional = true) {
217
+ object(v, p);
218
+ if (v.kind === "optional" && optional) {
219
+ fields(v, ["kind", "inner"], [], p);
220
+ valueType(v.inner, [...p, "inner"], false);
221
+ } else {
222
+ choice(v.kind, valueKinds, [...p, "kind"]);
223
+ fields(v, ["kind", ...["enum", "record"].includes(v.kind) ? ["name"] : []], [], p);
224
+ if (["enum", "record"].includes(v.kind)) text(v.name, [...p, "name"]);
225
+ }
226
+ if (Object.hasOwn(v, "source")) fail("IR_SCHEMA", "Type descriptors cannot carry source information.", p);
227
+ }
228
+ function ref(v, p, category) {
229
+ object(v, p);
230
+ if (Object.hasOwn(v, "source")) fail("IR_SCHEMA", "Resource references cannot carry source information.", p);
231
+ choice(v.kind, [
232
+ "pin",
233
+ "memory",
234
+ "parameter"
235
+ ], [...p, "kind"]);
236
+ if (v.kind === "parameter") {
237
+ fields(v, [
238
+ "kind",
239
+ "resource",
240
+ "index"
241
+ ], [], p);
242
+ choice(v.resource, ["pin", "memory"], [...p, "resource"]);
243
+ id(v.index, [...p, "index"]);
244
+ if (category && v.resource !== category) fail("IR_REFERENCE", "Resource category does not match.", p);
245
+ } else {
246
+ fields(v, ["kind", "name"], [], p);
247
+ text(v.name, [...p, "name"]);
248
+ if (category && v.kind !== category) fail("IR_REFERENCE", "Resource category does not match.", p);
249
+ }
250
+ }
251
+ function constant(v, p, pattern = false) {
252
+ object(v, p);
253
+ if (Object.hasOwn(v, "source")) fail("IR_SCHEMA", "Constants and patterns cannot carry source information.", p);
254
+ const choices = pattern ? [
255
+ "number",
256
+ "bool",
257
+ "string",
258
+ "enum",
259
+ "none"
260
+ ] : [
261
+ "number",
262
+ "bool",
263
+ "string",
264
+ "bytes",
265
+ "enum",
266
+ "record",
267
+ "none"
268
+ ];
269
+ choice(v.kind, choices, [...p, "kind"]);
270
+ switch (v.kind) {
271
+ case "none":
272
+ fields(v, ["kind"], [], p);
273
+ break;
274
+ case "enum":
275
+ fields(v, [
276
+ "kind",
277
+ "name",
278
+ "member"
279
+ ], [], p);
280
+ text(v.name, [...p, "name"]);
281
+ text(v.member, [...p, "member"]);
282
+ break;
283
+ case "record":
284
+ fields(v, [
285
+ "kind",
286
+ "name",
287
+ "fields"
288
+ ], [], p);
289
+ text(v.name, [...p, "name"]);
290
+ list(v.fields, [...p, "fields"], (f, q) => {
291
+ object(f, q);
292
+ fields(f, ["name", "value"], [], q);
293
+ text(f.name, [...q, "name"]);
294
+ if (!["number", "boolean"].includes(typeof f.value)) fail("IR_SCHEMA", "Expected a number or boolean field.", [...q, "value"]);
295
+ });
296
+ break;
297
+ case "bytes":
298
+ fields(v, ["kind", "value"], [], p);
299
+ list(v.value, [...p, "value"], (n, q) => {
300
+ if (!Number.isInteger(n) || n < 0 || n > 255) fail("IR_SCHEMA", "Expected a byte.", q);
301
+ });
302
+ break;
303
+ default:
304
+ fields(v, ["kind", "value"], [], p);
305
+ if (v.kind === "number") {
306
+ num(v.value, [...p, "value"]);
307
+ if (pattern && !Number.isFinite(v.value)) fail("IR_CONTROL_FLOW", "Numeric match patterns must be finite.", p);
308
+ } else if (v.kind === "bool") bool(v.value, [...p, "value"]);
309
+ else text(v.value, [...p, "value"]);
310
+ }
311
+ }
312
+ function region(v, p) {
313
+ object(v, p);
314
+ fields(v, ["operations"], [], p);
315
+ list(v.operations, [...p, "operations"], operation);
316
+ }
317
+ function computation(v, p) {
318
+ object(v, p);
319
+ fields(v, ["type", "body"], [], p);
320
+ valueType(v.type, [...p, "type"]);
321
+ region(v.body, [...p, "body"]);
322
+ }
323
+ function operation(v, p) {
324
+ object(v, p);
325
+ text(v.kind, [...p, "kind"]);
326
+ let required = [], optional = [], result = false;
327
+ const resultFields = (names) => {
328
+ required = names;
329
+ result = true;
330
+ };
331
+ switch (v.kind) {
332
+ case "constant":
333
+ resultFields(["constant"]);
334
+ constant(v.constant, [...p, "constant"]);
335
+ break;
336
+ case "read":
337
+ resultFields(["resource", "name"]);
338
+ choice(v.resource, [
339
+ "property",
340
+ "const",
341
+ "state",
342
+ "derived"
343
+ ], [...p, "resource"]);
344
+ text(v.name, [...p, "name"]);
345
+ break;
346
+ case "parameter":
347
+ case "reactionCapture":
348
+ resultFields(["index"]);
349
+ id(v.index, [...p, "index"]);
350
+ break;
351
+ case "reactionValue":
352
+ resultFields(["binding"]);
353
+ choice(v.binding, ["previous", "current"], [...p, "binding"]);
354
+ break;
355
+ case "unary":
356
+ resultFields(["operator", "value"]);
357
+ choice(v.operator, unary, [...p, "operator"]);
358
+ break;
359
+ case "binary":
360
+ resultFields([
361
+ "operator",
362
+ "left",
363
+ "right"
364
+ ]);
365
+ choice(v.operator, binary, [...p, "operator"]);
366
+ break;
367
+ case "numeric":
368
+ resultFields(["builtin", "args"]);
369
+ choice(v.builtin, numeric, [...p, "builtin"]);
370
+ list(v.args, [...p, "args"], id);
371
+ break;
372
+ case "record":
373
+ resultFields(["fields"]);
374
+ list(v.fields, [...p, "fields"], (f, q) => {
375
+ object(f, q);
376
+ fields(f, ["name", "value"], [], q);
377
+ text(f.name, [...q, "name"]);
378
+ id(f.value, [...q, "value"]);
379
+ });
380
+ break;
381
+ case "field":
382
+ resultFields(["value", "field"]);
383
+ text(v.field, [...p, "field"]);
384
+ break;
385
+ case "bytes":
386
+ resultFields(["elements"]);
387
+ list(v.elements, [...p, "elements"], id);
388
+ break;
389
+ case "length":
390
+ case "optional":
391
+ case "isNone":
392
+ case "nonNull":
393
+ resultFields(["value"]);
394
+ break;
395
+ case "index":
396
+ resultFields(["value", "index"]);
397
+ id(v.index, [...p, "index"]);
398
+ break;
399
+ case "localRead":
400
+ resultFields(["local"]);
401
+ id(v.local, [...p, "local"]);
402
+ break;
403
+ case "local":
404
+ required = [
405
+ "local",
406
+ "type",
407
+ "initial"
408
+ ];
409
+ id(v.local, [...p, "local"]);
410
+ valueType(v.type, [...p, "type"]);
411
+ break;
412
+ case "localWrite":
413
+ required = ["local", "value"];
414
+ id(v.local, [...p, "local"]);
415
+ break;
416
+ case "write":
417
+ required = [
418
+ "resource",
419
+ "name",
420
+ "value"
421
+ ];
422
+ choice(v.resource, ["state"], [...p, "resource"]);
423
+ text(v.name, [...p, "name"]);
424
+ break;
425
+ case "memoryLength":
426
+ resultFields(["memory"]);
427
+ ref(v.memory, [...p, "memory"], "memory");
428
+ break;
429
+ case "load8":
430
+ case "load16le":
431
+ case "load16be":
432
+ case "load32le":
433
+ case "load32be":
434
+ resultFields(["memory", "offset"]);
435
+ ref(v.memory, [...p, "memory"], "memory");
436
+ break;
437
+ case "store8":
438
+ case "store16le":
439
+ case "store16be":
440
+ case "store32le":
441
+ case "store32be":
442
+ required = [
443
+ "memory",
444
+ "offset",
445
+ "value"
446
+ ];
447
+ ref(v.memory, [...p, "memory"], "memory");
448
+ break;
449
+ case "copyBytes":
450
+ resultFields([
451
+ "memory",
452
+ "offset",
453
+ "length"
454
+ ]);
455
+ ref(v.memory, [...p, "memory"], "memory");
456
+ break;
457
+ case "readSample":
458
+ resultFields(["pin"]);
459
+ ref(v.pin, [...p, "pin"], "pin");
460
+ break;
461
+ case "resistor":
462
+ resultFields(["to", "resistance"]);
463
+ ref(v.to, [...p, "to"], "pin");
464
+ break;
465
+ case "combineIntents":
466
+ resultFields(["values"]);
467
+ list(v.values, [...p, "values"], id);
468
+ break;
469
+ case "refEqual":
470
+ resultFields(["left", "right"]);
471
+ ref(v.left, [...p, "left"]);
472
+ ref(v.right, [...p, "right"]);
473
+ break;
474
+ case "writeIntent":
475
+ required = ["pin", "value"];
476
+ ref(v.pin, [...p, "pin"], "pin");
477
+ break;
478
+ case "call":
479
+ case "callValue":
480
+ required = [
481
+ "name",
482
+ "pure",
483
+ "args"
484
+ ];
485
+ result = v.kind === "callValue";
486
+ text(v.name, [...p, "name"]);
487
+ bool(v.pure, [...p, "pure"]);
488
+ list(v.args, [...p, "args"], (a, q) => {
489
+ object(a, q);
490
+ if (a.kind === "value") {
491
+ fields(a, ["kind", "value"], [], q);
492
+ id(a.value, [...q, "value"]);
493
+ } else ref(a, q);
494
+ });
495
+ break;
496
+ case "send":
497
+ required = ["channel", "value"];
498
+ text(v.channel, [...p, "channel"]);
499
+ break;
500
+ case "notify":
501
+ required = ["notification"];
502
+ text(v.notification, [...p, "notification"]);
503
+ break;
504
+ case "receive":
505
+ resultFields(["channel"]);
506
+ text(v.channel, [...p, "channel"]);
507
+ break;
508
+ case "now":
509
+ case "random":
510
+ resultFields([]);
511
+ break;
512
+ case "if":
513
+ required = ["condition", "yes"];
514
+ optional = ["no"];
515
+ region(v.yes, [...p, "yes"]);
516
+ if (Object.hasOwn(v, "no")) region(v.no, [...p, "no"]);
517
+ break;
518
+ case "match":
519
+ required = ["value", "arms"];
520
+ optional = ["otherwise"];
521
+ list(v.arms, [...p, "arms"], (a, q) => {
522
+ object(a, q);
523
+ fields(a, ["pattern", "body"], [], q);
524
+ constant(a.pattern, [...q, "pattern"], true);
525
+ region(a.body, [...q, "body"]);
526
+ });
527
+ if (Object.hasOwn(v, "otherwise")) region(v.otherwise, [...p, "otherwise"]);
528
+ break;
529
+ case "while":
530
+ required = ["test", "body"];
531
+ computation(v.test, [...p, "test"]);
532
+ region(v.body, [...p, "body"]);
533
+ break;
534
+ case "loop":
535
+ required = ["body"];
536
+ region(v.body, [...p, "body"]);
537
+ break;
538
+ case "and":
539
+ case "or":
540
+ resultFields(["left", "right"]);
541
+ computation(v.right, [...p, "right"]);
542
+ break;
543
+ case "select":
544
+ resultFields([
545
+ "condition",
546
+ "yes",
547
+ "no"
548
+ ]);
549
+ computation(v.yes, [...p, "yes"]);
550
+ computation(v.no, [...p, "no"]);
551
+ break;
552
+ case "assert":
553
+ required = ["condition"];
554
+ optional = ["message"];
555
+ if (Object.hasOwn(v, "message")) computation(v.message, [...p, "message"]);
556
+ break;
557
+ case "wait":
558
+ case "elapse":
559
+ required = ["duration"];
560
+ break;
561
+ case "waitUntil":
562
+ required = ["predicate"];
563
+ text(v.predicate, [...p, "predicate"]);
564
+ break;
565
+ case "redirect":
566
+ required = ["target", "delay"];
567
+ break;
568
+ case "return":
569
+ case "break":
570
+ case "continue":
571
+ case "stop": break;
572
+ case "returnValue":
573
+ case "yield":
574
+ required = ["value"];
575
+ break;
576
+ default: fail("IR_SCHEMA", `Unknown operation ${v.kind}.`, [...p, "kind"]);
577
+ }
578
+ fields(v, [
579
+ "kind",
580
+ ...required,
581
+ ...result ? ["result", "type"] : []
582
+ ], optional, p);
583
+ if (result) {
584
+ id(v.result, [...p, "result"]);
585
+ valueType(v.type, [...p, "type"]);
586
+ }
587
+ for (const key of [
588
+ "value",
589
+ "left",
590
+ "right",
591
+ "condition",
592
+ "initial",
593
+ "offset",
594
+ "length",
595
+ "resistance",
596
+ "duration",
597
+ "target",
598
+ "delay"
599
+ ]) if (Object.hasOwn(v, key) && typeof v[key] !== "object" && !(key === "value" && ["constant", "read"].includes(v.kind))) id(v[key], [...p, key]);
600
+ else if (Object.hasOwn(v, key) && typeof v[key] === "object" && ![
601
+ "refEqual",
602
+ "and",
603
+ "or"
604
+ ].includes(v.kind)) fail("IR_SCHEMA", "Expected a result identifier.", [...p, key]);
605
+ }
606
+ function deviceSchema(v) {
607
+ object(v, []);
608
+ fields(v, [
609
+ "kind",
610
+ "name",
611
+ "declarations"
612
+ ], [], []);
613
+ choice(v.kind, ["device"], ["kind"]);
614
+ text(v.name, ["name"]);
615
+ list(v.declarations, ["declarations"], (d, p) => {
616
+ object(d, p);
617
+ text(d.kind, [...p, "kind"]);
618
+ const required = ["kind"];
619
+ let optional = [];
620
+ if (!["start", "assert"].includes(d.kind)) {
621
+ required.push("name");
622
+ text(d.name, [...p, "name"]);
623
+ }
624
+ switch (d.kind) {
625
+ case "enum":
626
+ required.push("members");
627
+ list(d.members, [...p, "members"], text);
628
+ break;
629
+ case "record":
630
+ required.push("fields");
631
+ list(d.fields, [...p, "fields"], (f, q) => {
632
+ object(f, q);
633
+ fields(f, ["name", "type"], [], q);
634
+ text(f.name, [...q, "name"]);
635
+ choice(f.type, ["number", "bool"], [...q, "type"]);
636
+ });
637
+ break;
638
+ case "property":
639
+ required.push("type");
640
+ optional = ["default"];
641
+ if (Object.hasOwn(d, "default")) computation(d.default, [...p, "default"]);
642
+ break;
643
+ case "const":
644
+ case "derived":
645
+ required.push("type", "value");
646
+ computation(d.value, [...p, "value"]);
647
+ break;
648
+ case "state":
649
+ required.push("type", "initial");
650
+ computation(d.initial, [...p, "initial"]);
651
+ break;
652
+ case "pin":
653
+ optional = [
654
+ "initial",
655
+ "low",
656
+ "high"
657
+ ];
658
+ for (const k of optional) if (Object.hasOwn(d, k)) computation(d[k], [...p, k]);
659
+ if (Object.hasOwn(d, "low") !== Object.hasOwn(d, "high")) fail("IR_SCHEMA", "Pin thresholds must be provided together.", p);
660
+ break;
661
+ case "memory":
662
+ required.push("public", "length");
663
+ bool(d.public, [...p, "public"]);
664
+ computation(d.length, [...p, "length"]);
665
+ break;
666
+ case "channel":
667
+ required.push("direction", "type", "capacity");
668
+ optional = ["maxLength", "overflow"];
669
+ computation(d.capacity, [...p, "capacity"]);
670
+ for (const k of optional) if (Object.hasOwn(d, k)) computation(d[k], [...p, k]);
671
+ break;
672
+ case "notification":
673
+ required.push("direction");
674
+ break;
675
+ case "function":
676
+ required.push("pure", "params", "body");
677
+ optional = ["returns"];
678
+ bool(d.pure, [...p, "pure"]);
679
+ list(d.params, [...p, "params"], (param, q) => {
680
+ object(param, q);
681
+ fields(param, ["name", "type"], [], q);
682
+ text(param.name, [...q, "name"]);
683
+ object(param.type, [...q, "type"]);
684
+ if (["pin", "memory"].includes(param.type.kind)) fields(param.type, ["kind"], [], [...q, "type"]);
685
+ else valueType(param.type, [...q, "type"]);
686
+ });
687
+ if (Object.hasOwn(d, "returns")) valueType(d.returns, [...p, "returns"]);
688
+ region(d.body, [...p, "body"]);
689
+ break;
690
+ case "start":
691
+ case "assert":
692
+ required.push("body");
693
+ region(d.body, [...p, "body"]);
694
+ break;
695
+ case "on":
696
+ required.push("sourcePath", "body");
697
+ list(d.sourcePath, [...p, "sourcePath"], text);
698
+ region(d.body, [...p, "body"]);
699
+ break;
700
+ case "reaction":
701
+ required.push("sourcePath", "sourceType", "previous", "current", "captures", "body");
702
+ list(d.sourcePath, [...p, "sourcePath"], text);
703
+ valueType(d.sourceType, [...p, "sourceType"]);
704
+ text(d.previous, [...p, "previous"]);
705
+ text(d.current, [...p, "current"]);
706
+ list(d.captures, [...p, "captures"], (c, q) => {
707
+ object(c, q);
708
+ fields(c, ["name", "value"], [], q);
709
+ text(c.name, [...q, "name"]);
710
+ computation(c.value, [...q, "value"]);
711
+ });
712
+ region(d.body, [...p, "body"]);
713
+ break;
714
+ default: fail("IR_SCHEMA", `Unknown declaration ${d.kind}.`, [...p, "kind"]);
715
+ }
716
+ fields(d, required, optional, p);
717
+ if (Object.hasOwn(d, "type")) valueType(d.type, [...p, "type"]);
718
+ if (["channel", "notification"].includes(d.kind)) choice(d.direction, ["input", "output"], [...p, "direction"]);
719
+ });
720
+ }
721
+ function blockSchema(v) {
722
+ object(v, []);
723
+ fields(v, ["kind", "instructions"], [], []);
724
+ choice(v.kind, ["block"], ["kind"]);
725
+ list(v.instructions, ["instructions"], (instruction, p) => {
726
+ object(instruction, p);
727
+ fields(instruction, [
728
+ "pc",
729
+ "nextPc",
730
+ "body"
731
+ ], [], p);
732
+ for (const key of ["pc", "nextPc"]) if (typeof instruction[key] !== "number" || !Number.isFinite(instruction[key])) fail("IR_SCHEMA", "Instruction positions must be finite numbers.", [...p, key]);
733
+ region(instruction.body, [...p, "body"]);
734
+ });
735
+ const instructions = v.instructions;
736
+ if (!instructions.length) fail("IR_JIT_BOUNDARY", "A block requires at least one instruction.", ["instructions"]);
737
+ for (let i = 1; i < instructions.length; i++) if (instructions[i - 1].nextPc !== instructions[i].pc) fail("IR_JIT_BOUNDARY", "Adjacent instruction boundaries do not match.", [
738
+ "instructions",
739
+ i,
740
+ "pc"
741
+ ]);
742
+ }
743
+ //#endregion
744
+ //#region src/validation.ts
745
+ var numberType = { kind: "number" };
746
+ var boolType = { kind: "bool" };
747
+ var stringType = { kind: "string" };
748
+ var bytesType = { kind: "bytes" };
749
+ var intentType = { kind: "intent" };
750
+ var sampleType = { kind: "sample" };
751
+ var reserved = /* @__PURE__ */ new Set([
752
+ "device",
753
+ "default",
754
+ "const",
755
+ "let",
756
+ "enum",
757
+ "record",
758
+ "state",
759
+ "derived",
760
+ "pin",
761
+ "memory",
762
+ "fn",
763
+ "pure",
764
+ "process",
765
+ "on",
766
+ "reaction",
767
+ "as",
768
+ "capture",
769
+ "if",
770
+ "else",
771
+ "match",
772
+ "while",
773
+ "loop",
774
+ "break",
775
+ "continue",
776
+ "return",
777
+ "await",
778
+ "assert",
779
+ "redirect",
780
+ "true",
781
+ "false",
782
+ "none",
783
+ "start",
784
+ "number",
785
+ "bool",
786
+ "string",
787
+ "bytes",
788
+ "sample",
789
+ "intent",
790
+ "NaN",
791
+ "Infinity",
792
+ ...Object.keys(builtins),
793
+ ...numeric,
794
+ ...loads,
795
+ ...stores,
796
+ "resistor",
797
+ "copyBytes",
798
+ "now",
799
+ "random",
800
+ "ticks"
801
+ ]);
802
+ function sameType(a, b) {
803
+ const left = a;
804
+ const right = b;
805
+ return left?.kind === right?.kind && (left?.kind === "optional" ? sameType(left.inner, right?.inner) : left?.kind === "enum" || left?.kind === "record" ? left.name === right?.name : true);
806
+ }
807
+ function inner(t) {
808
+ return t.kind === "optional" ? t.inner : t;
809
+ }
810
+ function comparable(t) {
811
+ return inner(t).kind !== "intent";
812
+ }
813
+ function copy(s) {
814
+ return {
815
+ facts: new Map(s.facts),
816
+ locals: new Map(s.locals)
817
+ };
818
+ }
819
+ function intersect(maps) {
820
+ const out = new Map(maps[0]);
821
+ for (const [k, v] of out) if (maps.some((map) => map.get(k) !== v)) out.delete(k);
822
+ return out;
823
+ }
824
+ function lexical(flow, visible) {
825
+ for (const state of [
826
+ flow.normal,
827
+ ...flow.breaks,
828
+ ...flow.continues,
829
+ ...flow.yieldStates
830
+ ]) if (state) {
831
+ for (const id of state.locals.keys()) if (!visible.has(id)) state.locals.delete(id);
832
+ }
833
+ return flow;
834
+ }
835
+ function merge(states) {
836
+ if (!states.length) return void 0;
837
+ const out = copy(states[0]);
838
+ out.facts = intersect(states.map((s) => s.facts));
839
+ for (const [id, slot] of out.locals) {
840
+ const candidates = states.map((s) => s.locals.get(id));
841
+ if (candidates.some((c) => !c)) {
842
+ out.locals.delete(id);
843
+ continue;
844
+ }
845
+ if (candidates.some((c) => c.value.key !== slot.value.key)) {
846
+ const info = {
847
+ type: slot.type,
848
+ key: `join:${id}:${candidates.map((c) => c.value.key).join("|")}`
849
+ };
850
+ const facts = candidates.map((c, i) => states[i].facts.get(c.value.key));
851
+ if (facts.every((f) => f === true)) out.facts.set(info.key, true);
852
+ if (facts.every((f) => f === false)) out.facts.set(info.key, false);
853
+ out.locals.set(id, {
854
+ type: slot.type,
855
+ value: info
856
+ });
857
+ }
858
+ }
859
+ return out;
860
+ }
861
+ function walk(region, visit, path = []) {
862
+ region.operations.forEach((op, i) => {
863
+ const p = [
864
+ ...path,
865
+ "operations",
866
+ i
867
+ ];
868
+ visit(op, p);
869
+ for (const key of [
870
+ "yes",
871
+ "no",
872
+ "otherwise",
873
+ "body"
874
+ ]) {
875
+ const nested = key in op ? op[key] : void 0;
876
+ if (nested) {
877
+ const valueRegion = "body" in nested;
878
+ walk(valueRegion ? nested.body : nested, visit, [
879
+ ...p,
880
+ key,
881
+ ...valueRegion ? ["body"] : []
882
+ ]);
883
+ }
884
+ }
885
+ if (op.kind === "while") walk(op.test.body, visit, [
886
+ ...p,
887
+ "test",
888
+ "body"
889
+ ]);
890
+ if (op.kind === "assert" && op.message) walk(op.message.body, visit, [
891
+ ...p,
892
+ "message",
893
+ "body"
894
+ ]);
895
+ if (op.kind === "and" || op.kind === "or") walk(op.right.body, visit, [
896
+ ...p,
897
+ "right",
898
+ "body"
899
+ ]);
900
+ if (op.kind === "match") op.arms.forEach((a, j) => walk(a.body, visit, [
901
+ ...p,
902
+ "arms",
903
+ j,
904
+ "body"
905
+ ]));
906
+ });
907
+ }
908
+ var Validator = class {
909
+ device;
910
+ declarations = /* @__PURE__ */ new Map();
911
+ paths = /* @__PURE__ */ new Map();
912
+ enums = new Map(Object.entries(builtins));
913
+ records = /* @__PURE__ */ new Map();
914
+ functions = /* @__PURE__ */ new Map();
915
+ effectChecked = /* @__PURE__ */ new Set();
916
+ receiveOwners = /* @__PURE__ */ new Map();
917
+ constructor(device) {
918
+ this.device = device;
919
+ if (!device) return;
920
+ this.name(device.name, ["name"]);
921
+ let starts = 0;
922
+ device.declarations.forEach((d, i) => {
923
+ const p = ["declarations", i];
924
+ if (d.kind === "start") {
925
+ if (++starts > 1) this.error("IR_REFERENCE", "Only one start process is allowed.", p);
926
+ return;
927
+ }
928
+ if (d.kind === "assert") return;
929
+ this.name(d.name, [...p, "name"]);
930
+ if (this.declarations.has(d.name)) this.error("IR_REFERENCE", `Duplicate declaration ${d.name}.`, p);
931
+ this.declarations.set(d.name, d);
932
+ this.paths.set(d.name, p);
933
+ if (d.kind === "enum") {
934
+ this.uniqueNames(d.members, [...p, "members"]);
935
+ if (!d.members.length) this.error("IR_TYPE", "An enum must have at least one member.", p);
936
+ this.enums.set(d.name, d.members);
937
+ }
938
+ if (d.kind === "record") {
939
+ this.uniqueNames(d.fields.map((f) => f.name), [...p, "fields"]);
940
+ this.records.set(d.name, d.fields);
941
+ }
942
+ if (d.kind === "function") this.functions.set(d.name, d);
943
+ });
944
+ }
945
+ error(code, message, path, node) {
946
+ return fail(code, message, path, "finalize", node?.source);
947
+ }
948
+ name(name, p) {
949
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || reserved.has(name)) this.error("IR_REFERENCE", `Invalid or reserved identifier ${name}.`, p);
950
+ }
951
+ uniqueNames(names, p, members = false) {
952
+ const seen = /* @__PURE__ */ new Set();
953
+ names.forEach((name, i) => {
954
+ if (members) {
955
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) this.error("IR_REFERENCE", "Invalid member name.", [...p, i]);
956
+ } else this.name(name, [...p, i]);
957
+ if (seen.has(name)) this.error("IR_REFERENCE", `Duplicate name ${name}.`, [...p, i]);
958
+ seen.add(name);
959
+ });
960
+ }
961
+ type(t, p) {
962
+ const v = inner(t);
963
+ if (v.kind === "enum" && !this.enums.has(v.name) && this.device) this.error("IR_REFERENCE", `Unknown enum ${v.name}.`, p);
964
+ if (v.kind === "record" && !this.records.has(v.name) && this.device) this.error("IR_REFERENCE", `Unknown record ${v.name}.`, p);
965
+ }
966
+ expect(actual, expected, p, node) {
967
+ if (!sameType(actual, expected)) this.error("IR_TYPE", `Expected ${this.describe(expected)}, received ${this.describe(actual)}.`, p, node);
968
+ }
969
+ describe(t) {
970
+ const type = t;
971
+ return !type ? "no value" : type.kind === "optional" ? `${this.describe(type.inner)}?` : String(type.name ?? type.kind);
972
+ }
973
+ declaration(name, kind, p) {
974
+ if (!this.device) return void 0;
975
+ const d = this.declarations.get(name);
976
+ if (!d || d.kind !== kind) this.error("IR_REFERENCE", `Expected ${kind} declaration ${name}.`, p);
977
+ return d;
978
+ }
979
+ ref(ref, category, c, p) {
980
+ const kind = ref.kind === "parameter" ? ref.resource : ref.kind;
981
+ if (category && kind !== category) this.error("IR_REFERENCE", "Resource category does not match.", p);
982
+ if (ref.kind === "parameter") {
983
+ const param = c.params[ref.index];
984
+ if (!param) this.error("IR_SCOPE", "Resource parameter is outside the current function.", p);
985
+ if (param.type.kind !== kind) this.error("IR_REFERENCE", "Resource parameter category does not match.", p);
986
+ } else this.declaration(ref.name, kind, p);
987
+ return kind;
988
+ }
989
+ field(t, field, p) {
990
+ if (t.kind === "sample") {
991
+ const fields = {
992
+ voltage: {
993
+ kind: "optional",
994
+ inner: numberType
995
+ },
996
+ level: {
997
+ kind: "enum",
998
+ name: "Logic"
999
+ },
1000
+ status: {
1001
+ kind: "enum",
1002
+ name: "SampleStatus"
1003
+ }
1004
+ };
1005
+ if (!fields[field]) this.error("IR_REFERENCE", `Unknown sample field ${field}.`, p);
1006
+ return fields[field];
1007
+ }
1008
+ if (t.kind !== "record") this.error("IR_TYPE", "Field access requires a record or sample.", p);
1009
+ const fields = this.records.get(t.name);
1010
+ if (!fields && !this.device) return numberType;
1011
+ const f = fields?.find((f) => f.name === field);
1012
+ if (!f) this.error("IR_REFERENCE", `Unknown record field ${field}.`, p);
1013
+ return { kind: f.type };
1014
+ }
1015
+ constant(k, t, p) {
1016
+ if (k.kind === "none") {
1017
+ if (t.kind !== "optional") this.error("IR_TYPE", "none requires an optional result type.", p);
1018
+ return;
1019
+ }
1020
+ if (t.kind === "optional") this.error("IR_TYPE", "Non-null constants require a nonoptional type and explicit optional lift.", p);
1021
+ if (k.kind !== t.kind || (k.kind === "enum" || k.kind === "record") && k.name !== ("name" in t ? t.name : void 0)) this.error("IR_TYPE", "Constant and result types do not match.", p);
1022
+ if (k.kind === "enum") {
1023
+ const members = this.enums.get(k.name);
1024
+ if (members && !members.includes(k.member)) this.error("IR_REFERENCE", `Unknown enum member ${k.member}.`, p);
1025
+ }
1026
+ if (k.kind === "record") this.recordFields(k.fields, t, p, (f) => ({ kind: typeof f.value === "boolean" ? "bool" : "number" }));
1027
+ }
1028
+ recordFields(fields, t, p, get) {
1029
+ if (t.kind !== "record") this.error("IR_TYPE", "Record construction requires a named record type.", p);
1030
+ const expected = this.records.get(t.name);
1031
+ const seen = /* @__PURE__ */ new Set();
1032
+ for (let i = 0; i < fields.length; i++) {
1033
+ const f = fields[i];
1034
+ if (seen.has(f.name)) this.error("IR_REFERENCE", "Duplicate record field.", [
1035
+ ...p,
1036
+ "fields",
1037
+ i
1038
+ ]);
1039
+ seen.add(f.name);
1040
+ if (expected) {
1041
+ const declaration = expected.find((e) => e.name === f.name);
1042
+ if (!declaration) this.error("IR_REFERENCE", `Unknown record field ${f.name}.`, [
1043
+ ...p,
1044
+ "fields",
1045
+ i
1046
+ ]);
1047
+ this.expect(get(f), { kind: declaration.type }, [
1048
+ ...p,
1049
+ "fields",
1050
+ i
1051
+ ]);
1052
+ } else get(f);
1053
+ }
1054
+ if (expected && expected.length !== fields.length) this.error("IR_TYPE", "Record fields are incomplete.", p);
1055
+ }
1056
+ context(mode, returns) {
1057
+ return {
1058
+ mode,
1059
+ params: [],
1060
+ ...returns ? { returns } : {},
1061
+ loop: 0,
1062
+ results: /* @__PURE__ */ new Map(),
1063
+ resultIds: /* @__PURE__ */ new Set(),
1064
+ localIds: /* @__PURE__ */ new Set(),
1065
+ tail: true,
1066
+ finalInstruction: true,
1067
+ computation: false
1068
+ };
1069
+ }
1070
+ body(body, c, p) {
1071
+ const out = this.region(body, c, {
1072
+ facts: /* @__PURE__ */ new Map(),
1073
+ locals: /* @__PURE__ */ new Map()
1074
+ }, p);
1075
+ if (c.returns && out.normal) this.error("IR_CONTROL_FLOW", "A value-returning body has a normal path without returnValue.", p, body);
1076
+ this.effects(body, c.mode, p);
1077
+ }
1078
+ computation(value, expected, mode, p) {
1079
+ this.type(value.type, [...p, "type"]);
1080
+ this.expect(value.type, expected, [...p, "type"]);
1081
+ const c = this.context(mode, expected);
1082
+ c.computation = true;
1083
+ this.body(value.body, c, [...p, "body"]);
1084
+ }
1085
+ conditioned(s, info, truth) {
1086
+ const out = copy(s), facts = truth ? info.yes : info.no;
1087
+ if (facts) for (const [key, value] of facts) out.facts.set(key, value);
1088
+ return out;
1089
+ }
1090
+ region(body, c, initial, p) {
1091
+ let state = copy(initial);
1092
+ const flow = {
1093
+ breaks: [],
1094
+ continues: [],
1095
+ yielded: [],
1096
+ yieldStates: []
1097
+ };
1098
+ const results = new Map(c.results);
1099
+ c = {
1100
+ ...c,
1101
+ results
1102
+ };
1103
+ for (let i = 0; i < body.operations.length; i++) {
1104
+ const op = body.operations[i], q = [
1105
+ ...p,
1106
+ "operations",
1107
+ i
1108
+ ];
1109
+ if (!state) this.error("IR_CONTROL_FLOW", "Operation follows an unconditional path terminator.", q, op);
1110
+ const s = state;
1111
+ const get = (id, expected) => {
1112
+ const info = results.get(id);
1113
+ if (!info) this.error("IR_SCOPE", `Result ${id} is not visible or is used before its definition.`, q, op);
1114
+ if (expected) this.expect(info.type, expected, q, op);
1115
+ return info;
1116
+ };
1117
+ const result = (t, details = {}) => {
1118
+ const output = op;
1119
+ this.expect(output.type, t, [...q, "type"], op);
1120
+ const value = {
1121
+ type: output.type,
1122
+ key: `v:${output.result}`,
1123
+ ...details
1124
+ };
1125
+ results.set(output.result, value);
1126
+ return value;
1127
+ };
1128
+ if ("result" in op) {
1129
+ this.type(op.type, [...q, "type"]);
1130
+ if (c.resultIds.has(op.result)) this.error("IR_SCOPE", `Duplicate result ${op.result}.`, [...q, "result"], op);
1131
+ c.resultIds.add(op.result);
1132
+ }
1133
+ const slot = (id) => {
1134
+ const v = s.locals.get(id);
1135
+ if (!v) this.error("IR_SCOPE", "Local is not visible or not initialized.", [...q, "local"], op);
1136
+ return v;
1137
+ };
1138
+ const child = (region, branch, key, overrides = {}) => lexical(this.region(region, {
1139
+ ...c,
1140
+ tail: c.tail && i === body.operations.length - 1,
1141
+ ...overrides
1142
+ }, branch, [...q, key]), branch.locals);
1143
+ const accumulate = (f) => {
1144
+ flow.breaks.push(...f.breaks);
1145
+ flow.continues.push(...f.continues);
1146
+ flow.yielded.push(...f.yielded);
1147
+ flow.yieldStates.push(...f.yieldStates);
1148
+ };
1149
+ const valueRegion = (value, expected, branch, key) => {
1150
+ this.type(value.type, [
1151
+ ...q,
1152
+ key,
1153
+ "type"
1154
+ ]);
1155
+ this.expect(value.type, expected, [
1156
+ ...q,
1157
+ key,
1158
+ "type"
1159
+ ]);
1160
+ const f = lexical(this.region(value.body, {
1161
+ ...c,
1162
+ valueRegion: expected,
1163
+ loop: 0,
1164
+ tail: false
1165
+ }, branch, [
1166
+ ...q,
1167
+ key,
1168
+ "body"
1169
+ ]), branch.locals);
1170
+ if (f.normal) this.error("IR_CONTROL_FLOW", "A value region has a normal path without yield.", [...q, key], value);
1171
+ return {
1172
+ flow: f,
1173
+ ...f.yielded.length ? { value: {
1174
+ type: expected,
1175
+ key: `region:${"result" in op ? op.result : void 0}:${key}`,
1176
+ yes: intersect(f.yielded.map((v) => v.yes ?? /* @__PURE__ */ new Map())),
1177
+ no: intersect(f.yielded.map((v) => v.no ?? /* @__PURE__ */ new Map()))
1178
+ } } : {}
1179
+ };
1180
+ };
1181
+ switch (op.kind) {
1182
+ case "constant": {
1183
+ this.constant(op.constant, op.type, [...q, "constant"]);
1184
+ const info = result(op.type, { none: op.constant.kind === "none" });
1185
+ if (op.constant.kind === "none") s.facts.set(info.key, false);
1186
+ break;
1187
+ }
1188
+ case "read":
1189
+ result(this.declaration(op.name, op.resource, q)?.type ?? op.type);
1190
+ break;
1191
+ case "parameter": {
1192
+ const param = c.params[op.index];
1193
+ if (!param) this.error("IR_SCOPE", "Parameter index is outside the current function.", q, op);
1194
+ if (param.type.kind === "pin" || param.type.kind === "memory") this.error("IR_TYPE", "Resource parameters cannot be read as values.", q, op);
1195
+ result(param.type, { key: `parameter:${op.index}` });
1196
+ break;
1197
+ }
1198
+ case "reactionValue":
1199
+ if (!c.reaction) this.error("IR_SCOPE", "Reaction bindings are only visible inside their response body.", q, op);
1200
+ result(c.reaction.sourceType, { key: `reaction:${op.binding}` });
1201
+ break;
1202
+ case "reactionCapture":
1203
+ if (!c.reaction) this.error("IR_SCOPE", "Reaction captures are only visible inside their response body.", q, op);
1204
+ if (!c.reaction.captures[op.index]) this.error("IR_SCOPE", "Reaction capture index is out of range.", q, op);
1205
+ result(c.reaction.captures[op.index].value.type, { key: `capture:${op.index}` });
1206
+ break;
1207
+ case "unary": {
1208
+ const info = get(op.value, op.operator === "not" ? boolType : numberType);
1209
+ result(op.operator === "not" ? boolType : numberType, op.operator === "not" ? {
1210
+ yes: info.no ?? /* @__PURE__ */ new Map(),
1211
+ no: info.yes ?? /* @__PURE__ */ new Map()
1212
+ } : {});
1213
+ break;
1214
+ }
1215
+ case "binary": {
1216
+ const left = get(op.left), right = get(op.right);
1217
+ if (["equal", "notEqual"].includes(op.operator)) {
1218
+ this.expect(left.type, right.type, q, op);
1219
+ if (!comparable(left.type) && !(left.type.kind === "optional" && (left.none || right.none))) this.error("IR_TYPE", "Intent values do not support content equality.", q, op);
1220
+ const tested = left.none ? right : right.none ? left : void 0;
1221
+ const equal = op.operator === "equal";
1222
+ result(boolType, tested ? {
1223
+ yes: /* @__PURE__ */ new Map([[tested.key, !equal]]),
1224
+ no: /* @__PURE__ */ new Map([[tested.key, equal]])
1225
+ } : {});
1226
+ } else if (op.operator === "concat") {
1227
+ get(op.left, stringType);
1228
+ get(op.right, stringType);
1229
+ result(stringType);
1230
+ } else {
1231
+ get(op.left, numberType);
1232
+ get(op.right, numberType);
1233
+ result([
1234
+ "lessThan",
1235
+ "lessThanOrEqual",
1236
+ "greaterThan",
1237
+ "greaterThanOrEqual"
1238
+ ].includes(op.operator) ? boolType : numberType);
1239
+ }
1240
+ break;
1241
+ }
1242
+ case "numeric":
1243
+ if (op.args.length !== ([
1244
+ "min",
1245
+ "max",
1246
+ "pow"
1247
+ ].includes(op.builtin) ? 2 : 1)) this.error("IR_TYPE", "Numeric builtin argument count does not match.", q, op);
1248
+ op.args.forEach((arg) => get(arg, numberType));
1249
+ result(op.builtin.startsWith("is") ? boolType : numberType);
1250
+ break;
1251
+ case "record":
1252
+ this.recordFields(op.fields, op.type, q, (f) => get(f.value).type);
1253
+ result(op.type);
1254
+ break;
1255
+ case "field": {
1256
+ const value = get(op.value), t = value.type;
1257
+ result(t.kind === "record" && !this.device ? op.type : this.field(t, op.field, q), { key: `field:${value.key}:${op.field}` });
1258
+ break;
1259
+ }
1260
+ case "bytes":
1261
+ op.elements.forEach((id) => get(id, numberType));
1262
+ result(bytesType);
1263
+ break;
1264
+ case "length": {
1265
+ const t = get(op.value).type;
1266
+ if (!["string", "bytes"].includes(t.kind)) this.error("IR_TYPE", "length requires string or bytes.", q, op);
1267
+ result(numberType);
1268
+ break;
1269
+ }
1270
+ case "index": {
1271
+ const t = get(op.value).type;
1272
+ if (!["string", "bytes"].includes(t.kind)) this.error("IR_TYPE", "index requires string or bytes.", q, op);
1273
+ get(op.index, numberType);
1274
+ result(t.kind === "string" ? stringType : numberType);
1275
+ break;
1276
+ }
1277
+ case "optional": {
1278
+ const t = get(op.value).type;
1279
+ if (t.kind === "optional") this.error("IR_TYPE", "Optional types cannot be nested.", q, op);
1280
+ const v = result({
1281
+ kind: "optional",
1282
+ inner: t
1283
+ });
1284
+ s.facts.set(v.key, true);
1285
+ break;
1286
+ }
1287
+ case "isNone": {
1288
+ const v = get(op.value);
1289
+ if (v.type.kind !== "optional") this.error("IR_TYPE", "isNone requires an optional value.", q, op);
1290
+ result(boolType, {
1291
+ yes: /* @__PURE__ */ new Map([[v.key, false]]),
1292
+ no: /* @__PURE__ */ new Map([[v.key, true]])
1293
+ });
1294
+ break;
1295
+ }
1296
+ case "nonNull": {
1297
+ const v = get(op.value);
1298
+ if (v.type.kind !== "optional" || s.facts.get(v.key) !== true) this.error("IR_TYPE", "nonNull requires a dominating non-null proof.", q, op);
1299
+ result(v.type.inner);
1300
+ break;
1301
+ }
1302
+ case "local": {
1303
+ if (c.localIds.has(op.local)) this.error("IR_SCOPE", "Duplicate local identifier.", q, op);
1304
+ c.localIds.add(op.local);
1305
+ this.type(op.type, [...q, "type"]);
1306
+ const info = get(op.initial, op.type);
1307
+ s.locals.set(op.local, {
1308
+ type: op.type,
1309
+ value: info
1310
+ });
1311
+ break;
1312
+ }
1313
+ case "localRead": {
1314
+ const local = slot(op.local);
1315
+ result(local.type, { ...local.value });
1316
+ break;
1317
+ }
1318
+ case "localWrite": {
1319
+ const local = slot(op.local);
1320
+ const info = get(op.value, local.type);
1321
+ s.locals.set(op.local, {
1322
+ type: local.type,
1323
+ value: info
1324
+ });
1325
+ break;
1326
+ }
1327
+ case "write": {
1328
+ const d = this.declaration(op.name, "state", q);
1329
+ get(op.value, d?.type);
1330
+ break;
1331
+ }
1332
+ case "memoryLength":
1333
+ this.ref(op.memory, "memory", c, q);
1334
+ result(numberType);
1335
+ break;
1336
+ case "load8":
1337
+ case "load16le":
1338
+ case "load16be":
1339
+ case "load32le":
1340
+ case "load32be":
1341
+ this.ref(op.memory, "memory", c, q);
1342
+ get(op.offset, numberType);
1343
+ result(numberType);
1344
+ break;
1345
+ case "store8":
1346
+ case "store16le":
1347
+ case "store16be":
1348
+ case "store32le":
1349
+ case "store32be":
1350
+ this.ref(op.memory, "memory", c, q);
1351
+ get(op.offset, numberType);
1352
+ get(op.value, numberType);
1353
+ break;
1354
+ case "copyBytes":
1355
+ this.ref(op.memory, "memory", c, q);
1356
+ get(op.offset, numberType);
1357
+ get(op.length, numberType);
1358
+ result(bytesType);
1359
+ break;
1360
+ case "readSample":
1361
+ this.ref(op.pin, "pin", c, q);
1362
+ result(sampleType);
1363
+ break;
1364
+ case "resistor":
1365
+ this.ref(op.to, "pin", c, q);
1366
+ get(op.resistance, numberType);
1367
+ result(intentType);
1368
+ break;
1369
+ case "combineIntents":
1370
+ op.values.forEach((id) => get(id, intentType));
1371
+ result(intentType);
1372
+ break;
1373
+ case "refEqual":
1374
+ this.expect({ kind: this.ref(op.left, void 0, c, q) }, { kind: this.ref(op.right, void 0, c, q) }, q);
1375
+ result(boolType);
1376
+ break;
1377
+ case "writeIntent":
1378
+ this.ref(op.pin, "pin", c, q);
1379
+ get(op.value, intentType);
1380
+ break;
1381
+ case "call":
1382
+ case "callValue": {
1383
+ const target = this.declaration(op.name, "function", q);
1384
+ if (target && op.pure && !target.pure) this.error("IR_EFFECT", "A pure call must target an explicitly pure function.", q, op);
1385
+ if (target && op.args.length !== target.params.length) this.error("IR_TYPE", "Call argument count does not match.", q, op);
1386
+ op.args.forEach((arg, index) => {
1387
+ const param = target?.params[index];
1388
+ if (arg.kind === "value") {
1389
+ if (param && (param.type.kind === "pin" || param.type.kind === "memory")) this.error("IR_TYPE", "Resource argument required.", q, op);
1390
+ get(arg.value, param?.type);
1391
+ } else {
1392
+ if (param && !(param.type.kind === "pin" || param.type.kind === "memory")) this.error("IR_TYPE", "Value argument required.", q, op);
1393
+ this.ref(arg, param?.type.kind, c, q);
1394
+ }
1395
+ });
1396
+ if (op.kind === "callValue") {
1397
+ if (target && !target.returns) this.error("IR_TYPE", "callValue requires a value-returning function.", q, op);
1398
+ result(target?.returns ?? op.type);
1399
+ } else if (target?.returns) this.error("IR_TYPE", "call cannot discard the return type of a value-returning function.", q, op);
1400
+ break;
1401
+ }
1402
+ case "send": {
1403
+ const channel = this.declaration(op.channel, "channel", q);
1404
+ if (channel?.direction === "input") this.error("IR_REFERENCE", "send requires an output channel.", q, op);
1405
+ get(op.value, channel?.type);
1406
+ break;
1407
+ }
1408
+ case "notify":
1409
+ if (this.declaration(op.notification, "notification", q)?.direction === "input") this.error("IR_REFERENCE", "notify requires an output notification.", q, op);
1410
+ break;
1411
+ case "receive": {
1412
+ const channel = this.declaration(op.channel, "channel", q);
1413
+ if (channel?.direction === "output") this.error("IR_REFERENCE", "receive requires an input channel.", q, op);
1414
+ result(channel?.type ?? op.type);
1415
+ break;
1416
+ }
1417
+ case "now":
1418
+ case "random":
1419
+ result(numberType);
1420
+ break;
1421
+ case "if": {
1422
+ const condition = get(op.condition, boolType);
1423
+ const yes = child(op.yes, this.conditioned(s, condition, true), "yes");
1424
+ const no = op.no ? child(op.no, this.conditioned(s, condition, false), "no") : {
1425
+ normal: this.conditioned(s, condition, false),
1426
+ breaks: [],
1427
+ continues: [],
1428
+ yielded: [],
1429
+ yieldStates: []
1430
+ };
1431
+ accumulate(yes);
1432
+ accumulate(no);
1433
+ state = merge([yes.normal, no.normal].filter((v) => !!v));
1434
+ break;
1435
+ }
1436
+ case "match": {
1437
+ const value = get(op.value), base = inner(value.type);
1438
+ const seen = /* @__PURE__ */ new Set();
1439
+ const branches = [];
1440
+ for (let j = 0; j < op.arms.length; j++) {
1441
+ const arm = op.arms[j], pattern = arm.pattern;
1442
+ this.constant(pattern, pattern.kind === "none" ? value.type : base, [
1443
+ ...q,
1444
+ "arms",
1445
+ j,
1446
+ "pattern"
1447
+ ]);
1448
+ const key = pattern.kind === "enum" ? `${pattern.name}.${pattern.member}` : `${pattern.kind}:${String("value" in pattern ? pattern.value : void 0)}`;
1449
+ if (seen.has(key)) this.error("IR_CONTROL_FLOW", "Duplicate match pattern.", [
1450
+ ...q,
1451
+ "arms",
1452
+ j
1453
+ ]);
1454
+ seen.add(key);
1455
+ const branch = copy(s);
1456
+ if (value.type.kind === "optional") branch.facts.set(value.key, pattern.kind !== "none");
1457
+ const f = lexical(this.region(arm.body, {
1458
+ ...c,
1459
+ tail: c.tail && i === body.operations.length - 1
1460
+ }, branch, [
1461
+ ...q,
1462
+ "arms",
1463
+ j,
1464
+ "body"
1465
+ ]), branch.locals);
1466
+ accumulate(f);
1467
+ if (f.normal) branches.push(f.normal);
1468
+ }
1469
+ const complete = (base.kind === "bool" ? seen.has("bool:true") && seen.has("bool:false") : base.kind === "enum" ? this.enums.get(base.name)?.every((m) => seen.has(`${base.name}.${m}`)) : false) && (value.type.kind !== "optional" || seen.has("none:undefined"));
1470
+ if (op.otherwise) {
1471
+ const branch = copy(s);
1472
+ if (value.type.kind === "optional" && seen.has("none:undefined")) branch.facts.set(value.key, true);
1473
+ const f = child(op.otherwise, branch, "otherwise");
1474
+ accumulate(f);
1475
+ if (f.normal) branches.push(f.normal);
1476
+ } else if (!complete) this.error("IR_CONTROL_FLOW", "Match requires exhaustive patterns or a default region.", q, op);
1477
+ state = merge(branches);
1478
+ break;
1479
+ }
1480
+ case "while":
1481
+ case "loop": {
1482
+ const written = /* @__PURE__ */ new Set();
1483
+ walk(op.body, (nested) => {
1484
+ if (nested.kind === "localWrite" && s.locals.has(nested.local)) written.add(nested.local);
1485
+ });
1486
+ if (op.kind === "while") walk(op.test.body, (nested) => {
1487
+ if (nested.kind === "localWrite" && s.locals.has(nested.local)) written.add(nested.local);
1488
+ });
1489
+ const header = (backedges) => {
1490
+ const next = copy(s), incoming = [s, ...backedges];
1491
+ for (const id of written) {
1492
+ const slot = s.locals.get(id);
1493
+ const value = {
1494
+ type: slot.type,
1495
+ key: `loop:${q.join(".")}:${id}`
1496
+ };
1497
+ next.locals.set(id, {
1498
+ type: slot.type,
1499
+ value
1500
+ });
1501
+ const facts = incoming.map((state) => {
1502
+ const v = state.locals.get(id)?.value;
1503
+ return v ? state.facts.get(v.key) : void 0;
1504
+ });
1505
+ if (facts.every((fact) => fact === true)) next.facts.set(value.key, true);
1506
+ else if (facts.every((fact) => fact === false)) next.facts.set(value.key, false);
1507
+ }
1508
+ return next;
1509
+ };
1510
+ let loopState = header([]);
1511
+ const resultIds = new Set(c.resultIds), localIds = new Set(c.localIds);
1512
+ let test;
1513
+ let f;
1514
+ for (;;) {
1515
+ c.resultIds.clear();
1516
+ for (const id of resultIds) c.resultIds.add(id);
1517
+ c.localIds.clear();
1518
+ for (const id of localIds) c.localIds.add(id);
1519
+ test = op.kind === "while" ? valueRegion(op.test, boolType, loopState, "test") : void 0;
1520
+ const afterTest = test ? merge(test.flow.yieldStates) ?? loopState : loopState;
1521
+ const entry = test?.value ? this.conditioned(afterTest, test.value, true) : afterTest;
1522
+ f = child(op.body, entry, "body", {
1523
+ loop: c.loop + 1,
1524
+ tail: false
1525
+ });
1526
+ const next = header([f.normal, ...f.continues].filter((value) => !!value));
1527
+ if ([...written].every((id) => {
1528
+ const key = next.locals.get(id).value.key;
1529
+ return next.facts.get(key) === loopState.facts.get(key);
1530
+ })) break;
1531
+ loopState = next;
1532
+ }
1533
+ flow.yielded.push(...f.yielded);
1534
+ flow.yieldStates.push(...f.yieldStates);
1535
+ const exits = [...f.breaks];
1536
+ if (op.kind === "while" && test?.value) {
1537
+ const afterTest = merge(test.flow.yieldStates) ?? loopState;
1538
+ exits.push(this.conditioned(afterTest, test.value, false));
1539
+ }
1540
+ state = merge(exits);
1541
+ break;
1542
+ }
1543
+ case "and":
1544
+ case "or": {
1545
+ const left = get(op.left, boolType), isAnd = op.kind === "and";
1546
+ const right = valueRegion(op.right, boolType, this.conditioned(s, left, isAnd), "right");
1547
+ const rightInfo = right.value;
1548
+ const combine = (a, b) => new Map([...a ?? [], ...b ?? []]);
1549
+ result(boolType, {
1550
+ yes: isAnd ? combine(left.yes, rightInfo?.yes) : intersect([left.yes ?? /* @__PURE__ */ new Map(), combine(left.no, rightInfo?.yes)]),
1551
+ no: isAnd ? intersect([left.no ?? /* @__PURE__ */ new Map(), combine(left.yes, rightInfo?.no)]) : combine(left.no, rightInfo?.no)
1552
+ });
1553
+ state = merge([this.conditioned(s, left, !isAnd), ...right.flow.yieldStates]);
1554
+ break;
1555
+ }
1556
+ case "select": {
1557
+ const condition = get(op.condition, boolType);
1558
+ const yes = valueRegion(op.yes, op.type, this.conditioned(s, condition, true), "yes");
1559
+ const no = valueRegion(op.no, op.type, this.conditioned(s, condition, false), "no");
1560
+ result(op.type);
1561
+ state = merge([...yes.flow.yieldStates, ...no.flow.yieldStates]);
1562
+ break;
1563
+ }
1564
+ case "assert": {
1565
+ const condition = get(op.condition, boolType);
1566
+ if (op.message) valueRegion(op.message, stringType, this.conditioned(s, condition, false), "message");
1567
+ state = this.conditioned(s, condition, true);
1568
+ break;
1569
+ }
1570
+ case "wait":
1571
+ if (c.valueRegion || c.mode !== "process") this.error("IR_EFFECT", "wait is only allowed in a process statement region.", q, op);
1572
+ get(op.duration, numberType);
1573
+ break;
1574
+ case "elapse":
1575
+ if (c.valueRegion || c.mode !== "jit") this.error("IR_EFFECT", "elapse is only allowed in a JIT statement region.", q, op);
1576
+ get(op.duration, numberType);
1577
+ break;
1578
+ case "waitUntil":
1579
+ if (c.mode !== "jit") this.error("IR_EFFECT", "waitUntil requires a JIT instruction.", q, op);
1580
+ if (c.valueRegion || c.loop || !c.tail || i !== body.operations.length - 1 || !c.finalInstruction) this.error("IR_JIT_BOUNDARY", "waitUntil must be on the final instruction tail path.", q, op);
1581
+ {
1582
+ const fn = this.declaration(op.predicate, "function", q);
1583
+ if (fn && (!fn.pure || fn.params.length || !sameType(fn.returns, boolType))) this.error("IR_TYPE", "waitUntil requires a zero-parameter pure bool function.", q, op);
1584
+ }
1585
+ state = void 0;
1586
+ break;
1587
+ case "redirect":
1588
+ if (!["jit", "sync"].includes(c.mode)) this.error("IR_EFFECT", "redirect is not permitted in this context.", q, op);
1589
+ get(op.target, numberType);
1590
+ get(op.delay, numberType);
1591
+ state = void 0;
1592
+ break;
1593
+ case "stop":
1594
+ if (c.mode !== "jit" || c.valueRegion) this.error("IR_EFFECT", "stop requires a JIT statement region.", q, op);
1595
+ state = void 0;
1596
+ break;
1597
+ case "return":
1598
+ if (c.valueRegion || c.returns || c.mode === "jit" || c.computation || c.mode === "creation" || c.mode === "initial") this.error("IR_CONTROL_FLOW", "Void return is not valid in this body.", q, op);
1599
+ state = void 0;
1600
+ break;
1601
+ case "returnValue":
1602
+ if (c.valueRegion || !c.returns) this.error("IR_CONTROL_FLOW", "returnValue is not valid in this body.", q, op);
1603
+ get(op.value, c.returns);
1604
+ state = void 0;
1605
+ break;
1606
+ case "yield":
1607
+ if (!c.valueRegion) this.error("IR_CONTROL_FLOW", "yield requires a value region.", q, op);
1608
+ {
1609
+ const value = get(op.value, c.valueRegion);
1610
+ flow.yielded.push(value);
1611
+ flow.yieldStates.push(s);
1612
+ }
1613
+ state = void 0;
1614
+ break;
1615
+ case "break":
1616
+ case "continue":
1617
+ if (!c.loop) this.error("IR_CONTROL_FLOW", `${op.kind} requires an enclosing loop in this region.`, q, op);
1618
+ (op.kind === "break" ? flow.breaks : flow.continues).push(s);
1619
+ state = void 0;
1620
+ }
1621
+ }
1622
+ if (state) flow.normal = state;
1623
+ return flow;
1624
+ }
1625
+ effects(body, mode, path) {
1626
+ walk(body, (op, p) => {
1627
+ const pure = [
1628
+ "creation",
1629
+ "initial",
1630
+ "pure"
1631
+ ].includes(mode), creation = ["creation", "initial"].includes(mode);
1632
+ const effect = [
1633
+ "write",
1634
+ "writeIntent",
1635
+ "send",
1636
+ "notify",
1637
+ "now",
1638
+ "random",
1639
+ "wait",
1640
+ "receive",
1641
+ "elapse",
1642
+ "waitUntil",
1643
+ "stop",
1644
+ "redirect",
1645
+ ...stores
1646
+ ].includes(op.kind);
1647
+ if (pure && effect) this.error("IR_EFFECT", `${op.kind} is not allowed in ${mode} code.`, p, op);
1648
+ if (creation && (op.kind === "read" && !["property", "const"].includes(op.resource) || [
1649
+ "readSample",
1650
+ "memoryLength",
1651
+ "copyBytes",
1652
+ ...loads
1653
+ ].includes(op.kind))) this.error("IR_EFFECT", "Creation computations cannot read runtime resources.", p, op);
1654
+ if (mode === "creation" && ([
1655
+ "resistor",
1656
+ "combineIntents",
1657
+ "refEqual"
1658
+ ].includes(op.kind) || (op.kind === "call" || op.kind === "callValue") && op.args.some((a) => a.kind !== "value"))) this.error("IR_EFFECT", "Ordinary creation computations cannot use resource references or intents.", p, op);
1659
+ if (["reaction", "process"].includes(mode) && op.kind === "redirect") this.error("IR_EFFECT", "Control redirection requires a bound JIT entry.", p, op);
1660
+ if ([
1661
+ "sync",
1662
+ "reaction",
1663
+ "pure"
1664
+ ].includes(mode) && [
1665
+ "wait",
1666
+ "receive",
1667
+ "elapse",
1668
+ "waitUntil",
1669
+ "stop"
1670
+ ].includes(op.kind)) this.error("IR_EFFECT", "Synchronous code cannot suspend or use JIT control.", p, op);
1671
+ if (mode === "process" && [
1672
+ "elapse",
1673
+ "waitUntil",
1674
+ "stop"
1675
+ ].includes(op.kind)) this.error("IR_EFFECT", "Process code cannot use JIT control.", p, op);
1676
+ if (mode === "jit" && ["wait", "receive"].includes(op.kind)) this.error("IR_EFFECT", "JIT code cannot use process suspension.", p, op);
1677
+ if (op.kind === "call" || op.kind === "callValue" || op.kind === "waitUntil") {
1678
+ const name = op.kind === "waitUntil" ? op.predicate : op.name;
1679
+ const fn = this.functions.get(name);
1680
+ if (fn && pure && !fn.pure) this.error("IR_EFFECT", "Pure contexts can only call explicitly pure functions.", p, op);
1681
+ if (fn) {
1682
+ const key = `${mode}:${name}`;
1683
+ if (!this.effectChecked.has(key)) {
1684
+ this.effectChecked.add(key);
1685
+ this.effects(fn.body, mode, [...this.paths.get(name) ?? [], "body"]);
1686
+ }
1687
+ }
1688
+ }
1689
+ }, path);
1690
+ }
1691
+ source(path, p, notification) {
1692
+ if (!path.length) this.error("IR_SCHEMA", "An observation source path cannot be empty.", p);
1693
+ const declaration = this.declarations.get(path[0]);
1694
+ if (!declaration) this.error("IR_REFERENCE", "Observation source does not exist.", p);
1695
+ let type;
1696
+ let start = 1;
1697
+ if (declaration.kind === "notification" && notification) {
1698
+ if (declaration.direction !== "input" || path.length !== 1) this.error("IR_REFERENCE", "Only input notifications can trigger a process.", p);
1699
+ return;
1700
+ }
1701
+ if (declaration.kind === "pin") {
1702
+ if (path[1] !== "sample") this.error("IR_REFERENCE", "Pin observation must select sample.", p);
1703
+ type = sampleType;
1704
+ start = 2;
1705
+ } else if (declaration.kind === "state" || declaration.kind === "derived") type = declaration.type;
1706
+ else this.error("IR_REFERENCE", "An observation source must be state, derived, pin.sample or an input notification.", p);
1707
+ for (let i = start; i < path.length; i++) type = this.field(type, path[i], [...p, i]);
1708
+ if (!comparable(type)) this.error("IR_TYPE", "Observation sources must be comparable.", p);
1709
+ return type;
1710
+ }
1711
+ cycle(graph, label) {
1712
+ const active = /* @__PURE__ */ new Set(), done = /* @__PURE__ */ new Set();
1713
+ const visit = (name) => {
1714
+ if (active.has(name)) this.error("IR_DEPENDENCY", `${label} dependency cycle includes ${name}.`, this.paths.get(name) ?? []);
1715
+ if (done.has(name)) return;
1716
+ active.add(name);
1717
+ for (const next of graph.get(name) ?? []) if (graph.has(next)) visit(next);
1718
+ active.delete(name);
1719
+ done.add(name);
1720
+ };
1721
+ for (const key of graph.keys()) visit(key);
1722
+ }
1723
+ dependencies(body, resources, seen = /* @__PURE__ */ new Set()) {
1724
+ const deps = /* @__PURE__ */ new Set();
1725
+ walk(body, (op) => {
1726
+ if (op.kind === "read" && resources.includes(op.resource)) deps.add(op.name);
1727
+ if ((op.kind === "call" || op.kind === "callValue") && !seen.has(op.name)) {
1728
+ seen.add(op.name);
1729
+ const fn = this.functions.get(op.name);
1730
+ if (fn) for (const name of this.dependencies(fn.body, resources, seen)) deps.add(name);
1731
+ }
1732
+ });
1733
+ return deps;
1734
+ }
1735
+ validateDevice() {
1736
+ const graph = /* @__PURE__ */ new Map();
1737
+ for (const [name, fn] of this.functions) {
1738
+ const targets = /* @__PURE__ */ new Set();
1739
+ walk(fn.body, (op) => {
1740
+ if (op.kind === "call" || op.kind === "callValue") targets.add(op.name);
1741
+ });
1742
+ graph.set(name, targets);
1743
+ }
1744
+ this.cycle(graph, "Synchronous call");
1745
+ const creationGraph = /* @__PURE__ */ new Map(), derivedGraph = /* @__PURE__ */ new Map();
1746
+ this.device.declarations.forEach((d, i) => {
1747
+ const p = ["declarations", i];
1748
+ if ("type" in d && d.type) this.type(d.type, [...p, "type"]);
1749
+ if (d.kind === "property" || d.kind === "const" || d.kind === "state") {
1750
+ const base = inner(d.type);
1751
+ if (["sample", "intent"].includes(base.kind) || d.kind === "property" && base.kind === "record") this.error("IR_TYPE", "Type is not valid at this declaration position.", [...p, "type"], d);
1752
+ const key = d.kind === "property" ? "default" : d.kind === "const" ? "value" : "initial";
1753
+ const initialization = d.kind === "property" ? d.default : d.kind === "const" ? d.value : d.initial;
1754
+ if (initialization) {
1755
+ this.computation(initialization, d.type, "creation", [...p, key]);
1756
+ creationGraph.set(d.name, this.dependencies(initialization.body, ["property", "const"]));
1757
+ }
1758
+ } else if (d.kind === "derived") {
1759
+ this.computation(d.value, d.type, "pure", [...p, "value"]);
1760
+ derivedGraph.set(d.name, this.dependencies(d.value.body, ["derived"]));
1761
+ } else if (d.kind === "pin") {
1762
+ for (const key of ["low", "high"]) if (d[key]) this.computation(d[key], numberType, "creation", [...p, key]);
1763
+ if (d.initial) this.computation(d.initial, intentType, "initial", [...p, "initial"]);
1764
+ } else if (d.kind === "memory") this.computation(d.length, numberType, "creation", [...p, "length"]);
1765
+ else if (d.kind === "channel") {
1766
+ if (![
1767
+ "number",
1768
+ "bool",
1769
+ "record",
1770
+ "bytes"
1771
+ ].includes(d.type.kind)) this.error("IR_TYPE", "Invalid channel payload type.", [...p, "type"], d);
1772
+ if (d.type.kind === "bytes" !== !!d.maxLength) this.error("IR_TYPE", "Only bytes channels require maxLength.", p, d);
1773
+ this.computation(d.capacity, numberType, "creation", [...p, "capacity"]);
1774
+ if (d.maxLength) this.computation(d.maxLength, numberType, "creation", [...p, "maxLength"]);
1775
+ if (d.overflow) this.computation(d.overflow, {
1776
+ kind: "enum",
1777
+ name: "Overflow"
1778
+ }, "creation", [...p, "overflow"]);
1779
+ } else if (d.kind === "function") {
1780
+ this.uniqueNames(d.params.map((param) => param.name), [...p, "params"]);
1781
+ d.params.forEach((param, j) => {
1782
+ if (this.declarations.has(param.name)) this.error("IR_REFERENCE", "A parameter cannot shadow a device member.", [
1783
+ ...p,
1784
+ "params",
1785
+ j
1786
+ ]);
1787
+ if (!(param.type.kind === "pin" || param.type.kind === "memory")) this.type(param.type, [
1788
+ ...p,
1789
+ "params",
1790
+ j,
1791
+ "type"
1792
+ ]);
1793
+ });
1794
+ if (d.returns) this.type(d.returns, [...p, "returns"]);
1795
+ const c = this.context(d.pure ? "pure" : "sync", d.returns);
1796
+ c.params = d.params;
1797
+ this.body(d.body, c, [...p, "body"]);
1798
+ } else if (d.kind === "start" || d.kind === "on") {
1799
+ if (d.kind === "on") this.source(d.sourcePath, [...p, "sourcePath"], true);
1800
+ this.body(d.body, this.context("process"), [...p, "body"]);
1801
+ walk(d.body, (op) => {
1802
+ if (op.kind === "receive") {
1803
+ const owner = `${i}`;
1804
+ const old = this.receiveOwners.get(op.channel);
1805
+ if (old && old !== owner) {
1806
+ const error = new BuildError([{
1807
+ code: "IR_REFERENCE",
1808
+ message: "An input channel cannot have multiple consumer processes.",
1809
+ phase: "finalize",
1810
+ path: p,
1811
+ related: []
1812
+ }]);
1813
+ Object.defineProperty(error, "code", { value: "CHANNEL_CONSUMER_CONFLICT" });
1814
+ throw error;
1815
+ }
1816
+ this.receiveOwners.set(op.channel, owner);
1817
+ }
1818
+ });
1819
+ } else if (d.kind === "reaction") {
1820
+ const source = this.source(d.sourcePath, [...p, "sourcePath"], false);
1821
+ this.expect(d.sourceType, source, [...p, "sourceType"]);
1822
+ this.type(d.sourceType, [...p, "sourceType"]);
1823
+ const names = [
1824
+ d.previous,
1825
+ d.current,
1826
+ ...d.captures.map((capture) => capture.name)
1827
+ ];
1828
+ d.captures.forEach((capture, j) => {
1829
+ if (!comparable(capture.value.type)) this.error("IR_TYPE", "Capture results must be comparable.", [
1830
+ ...p,
1831
+ "captures",
1832
+ j
1833
+ ]);
1834
+ this.computation(capture.value, capture.value.type, "pure", [
1835
+ ...p,
1836
+ "captures",
1837
+ j,
1838
+ "value"
1839
+ ]);
1840
+ });
1841
+ const c = this.context("reaction");
1842
+ c.reaction = d;
1843
+ this.body(d.body, c, [...p, "body"]);
1844
+ this.uniqueNames(names, p);
1845
+ for (const name of names) if (this.declarations.has(name)) this.error("IR_REFERENCE", "Reaction bindings cannot shadow device members.", p);
1846
+ } else if (d.kind === "assert") this.body(d.body, this.context("creation"), [...p, "body"]);
1847
+ });
1848
+ this.cycle(creationGraph, "Creation");
1849
+ this.cycle(derivedGraph, "Derived");
1850
+ }
1851
+ validateBlock(block) {
1852
+ block.instructions.forEach((instruction, i) => {
1853
+ const c = this.context("jit");
1854
+ c.finalInstruction = i === block.instructions.length - 1;
1855
+ this.body(instruction.body, c, [
1856
+ "instructions",
1857
+ i,
1858
+ "body"
1859
+ ]);
1860
+ });
1861
+ }
1862
+ };
1863
+ function locateError(error, input) {
1864
+ if (!(error instanceof BuildError)) throw error;
1865
+ const located = new BuildError(error.diagnostics.map((diagnostic) => {
1866
+ if (diagnostic.source) return diagnostic;
1867
+ let node = input, source;
1868
+ const consider = () => {
1869
+ const candidate = node?.source;
1870
+ if (candidate && typeof candidate === "object" && (typeof candidate.label === "string" && candidate.uri === void 0 && candidate.range === void 0 || typeof candidate.uri === "string" && candidate.range?.start?.line >= 1 && candidate.range?.start?.column >= 1 && candidate.range?.end?.line >= 1 && candidate.range?.end?.column >= 1)) source = candidate;
1871
+ };
1872
+ consider();
1873
+ for (const part of diagnostic.path) {
1874
+ node = node?.[part];
1875
+ consider();
1876
+ }
1877
+ return source ? {
1878
+ ...diagnostic,
1879
+ source
1880
+ } : diagnostic;
1881
+ }));
1882
+ const runtimeCode = Object.getOwnPropertyDescriptor(error, "code");
1883
+ if (runtimeCode?.value === "CHANNEL_CONSUMER_CONFLICT") Object.defineProperty(located, "code", runtimeCode);
1884
+ throw located;
1885
+ }
1886
+ var validatedDevices = /* @__PURE__ */ new WeakSet();
1887
+ var validatedBlocks = /* @__PURE__ */ new WeakSet();
1888
+ var boundBlocks = /* @__PURE__ */ new WeakMap();
1889
+ function validateDeviceIR(input) {
1890
+ if (typeof input === "object" && input !== null && validatedDevices.has(input)) return input;
1891
+ const captured = snapshot(input);
1892
+ try {
1893
+ deviceSchema(captured);
1894
+ new Validator(captured).validateDevice();
1895
+ const result = captured;
1896
+ validatedDevices.add(result);
1897
+ return result;
1898
+ } catch (error) {
1899
+ return locateError(error, captured);
1900
+ }
1901
+ }
1902
+ function validateBlockIR(input) {
1903
+ if (typeof input === "object" && input !== null && validatedBlocks.has(input)) return input;
1904
+ const captured = snapshot(input);
1905
+ try {
1906
+ blockSchema(captured);
1907
+ new Validator().validateBlock(captured);
1908
+ const result = captured;
1909
+ validatedBlocks.add(result);
1910
+ return result;
1911
+ } catch (error) {
1912
+ return locateError(error, captured);
1913
+ }
1914
+ }
1915
+ /** Adds instance-definition reference and function-signature checks to a structurally valid block. */
1916
+ function validateBoundBlockIR(input, device) {
1917
+ try {
1918
+ const captured = validateBlockIR(input);
1919
+ const definition = validateDeviceIR(device);
1920
+ if (boundBlocks.get(captured)?.has(definition)) return captured;
1921
+ new Validator(definition).validateBlock(captured);
1922
+ let definitions = boundBlocks.get(captured);
1923
+ if (!definitions) {
1924
+ definitions = /* @__PURE__ */ new WeakSet();
1925
+ boundBlocks.set(captured, definitions);
1926
+ }
1927
+ definitions.add(definition);
1928
+ return captured;
1929
+ } catch (error) {
1930
+ if (error instanceof BuildError) {
1931
+ const bound = new BuildError(error.diagnostics.map((d) => ({
1932
+ ...d,
1933
+ phase: "bind"
1934
+ })));
1935
+ const runtimeCode = Object.getOwnPropertyDescriptor(error, "code");
1936
+ if (runtimeCode?.value === "CHANNEL_CONSUMER_CONFLICT") Object.defineProperty(bound, "code", runtimeCode);
1937
+ throw bound;
1938
+ }
1939
+ throw error;
1940
+ }
1941
+ }
1942
+ //#endregion
1943
+ //#region src/builder.ts
1944
+ var typeData = /* @__PURE__ */ new WeakMap();
1945
+ var values = /* @__PURE__ */ new WeakMap();
1946
+ var locals = /* @__PURE__ */ new WeakMap();
1947
+ var refs = /* @__PURE__ */ new WeakMap();
1948
+ var enumValues = /* @__PURE__ */ new WeakMap();
1949
+ var primitive = (kind) => {
1950
+ const descriptor = Object.freeze({});
1951
+ typeData.set(descriptor, { type: { kind } });
1952
+ return descriptor;
1953
+ };
1954
+ function enumType(name, members, owner) {
1955
+ const membersObject = {};
1956
+ for (const member of members) {
1957
+ const token = Object.freeze({});
1958
+ enumValues.set(token, {
1959
+ name,
1960
+ member,
1961
+ ...owner ? { owner } : {}
1962
+ });
1963
+ Object.defineProperty(membersObject, member, {
1964
+ value: token,
1965
+ enumerable: true
1966
+ });
1967
+ }
1968
+ const descriptor = Object.freeze({
1969
+ name,
1970
+ members: Object.freeze(membersObject)
1971
+ });
1972
+ typeData.set(descriptor, {
1973
+ type: {
1974
+ kind: "enum",
1975
+ name
1976
+ },
1977
+ members: Object.freeze([...members]),
1978
+ ...owner ? { owner } : {}
1979
+ });
1980
+ return descriptor;
1981
+ }
1982
+ function recordType(name, fields, owner) {
1983
+ const descriptor = Object.freeze({
1984
+ name,
1985
+ fields: Object.freeze({ ...fields })
1986
+ });
1987
+ typeData.set(descriptor, {
1988
+ type: {
1989
+ kind: "record",
1990
+ name
1991
+ },
1992
+ fields: descriptor.fields,
1993
+ ...owner ? { owner } : {}
1994
+ });
1995
+ return descriptor;
1996
+ }
1997
+ var factory = {
1998
+ number: primitive("number"),
1999
+ bool: primitive("bool"),
2000
+ string: primitive("string"),
2001
+ bytes: primitive("bytes"),
2002
+ intent: primitive("intent"),
2003
+ sample: primitive("sample"),
2004
+ pin: Object.freeze({ kind: "pin" }),
2005
+ memory: Object.freeze({ kind: "memory" }),
2006
+ logic: enumType("Logic", builtins.Logic),
2007
+ sampleStatus: enumType("SampleStatus", builtins.SampleStatus),
2008
+ overflow: enumType("Overflow", builtins.Overflow),
2009
+ optional(descriptor) {
2010
+ const info = typeData.get(descriptor);
2011
+ if (!info || info.type.kind === "optional") fail("IR_TYPE", "optional requires a nonoptional value type.", [], "construct");
2012
+ const result = Object.freeze({});
2013
+ typeData.set(result, {
2014
+ ...info,
2015
+ type: {
2016
+ kind: "optional",
2017
+ inner: info.type
2018
+ }
2019
+ });
2020
+ return result;
2021
+ },
2022
+ namedEnum(name, members) {
2023
+ if (typeof name !== "string" || !Array.isArray(members) || !members.length || members.some((m) => typeof m !== "string") || new Set(members).size !== members.length) fail("IR_TYPE", "Invalid named enum description.", [], "construct");
2024
+ return enumType(name, members);
2025
+ },
2026
+ namedRecord(name, fields) {
2027
+ if (typeof name !== "string" || !fields || typeof fields !== "object" || Array.isArray(fields)) fail("IR_TYPE", "Invalid named record description.", [], "construct");
2028
+ for (const field of Object.values(fields)) if (!["number", "bool"].includes(typeData.get(field)?.type.kind ?? "")) fail("IR_TYPE", "Record fields must be number or bool types.", [], "construct");
2029
+ return recordType(name, fields);
2030
+ }
2031
+ };
2032
+ var types = Object.freeze(factory);
2033
+ var n = { kind: "number" };
2034
+ var b = { kind: "bool" };
2035
+ var str = { kind: "string" };
2036
+ var bytes = { kind: "bytes" };
2037
+ var intent = { kind: "intent" };
2038
+ var sample = { kind: "sample" };
2039
+ function descendant(scope, parent) {
2040
+ for (let s = scope; s; s = s.parent) if (s === parent) return true;
2041
+ return false;
2042
+ }
2043
+ function infoOf(value) {
2044
+ return typeof value === "object" && value !== null ? values.get(value) : void 0;
2045
+ }
2046
+ function enumOf(value) {
2047
+ return typeof value === "object" && value !== null ? enumValues.get(value) : void 0;
2048
+ }
2049
+ function referenceOf(value) {
2050
+ return typeof value === "object" && value !== null ? refs.get(value) : void 0;
2051
+ }
2052
+ function fresh(map, info) {
2053
+ const handle = Object.freeze({});
2054
+ map.set(handle, info);
2055
+ return handle;
2056
+ }
2057
+ function alias(value) {
2058
+ return value.alias ? alias(value.alias) : value;
2059
+ }
2060
+ function joinFacts(branches) {
2061
+ const result = new Map(branches[0]);
2062
+ for (const [k, v] of result) if (branches.some((f) => f.get(k) !== v)) result.delete(k);
2063
+ return result;
2064
+ }
2065
+ var definitionOwners = /* @__PURE__ */ new WeakMap();
2066
+ var Root = class {
2067
+ options;
2068
+ device;
2069
+ jit;
2070
+ closed = false;
2071
+ active;
2072
+ source;
2073
+ declarations = [];
2074
+ names = /* @__PURE__ */ new Map();
2075
+ expectedTypes = /* @__PURE__ */ new Set();
2076
+ pending = [];
2077
+ instructions = [];
2078
+ constructor(options, device, jit = false) {
2079
+ this.options = options;
2080
+ this.device = device;
2081
+ this.jit = jit;
2082
+ if (options.source !== void 0) this.source = snapshot(options.source);
2083
+ }
2084
+ error(code, message, path = []) {
2085
+ this.closed = true;
2086
+ return fail(code, message, path, "construct", this.active?.source ?? this.source);
2087
+ }
2088
+ check() {
2089
+ if (this.closed) this.error("IR_LIFECYCLE", "This Builder is closed.");
2090
+ }
2091
+ guard(action) {
2092
+ this.check();
2093
+ try {
2094
+ return action();
2095
+ } catch (error) {
2096
+ this.closed = true;
2097
+ if (error instanceof BuildError && error.diagnostics.some((d) => d.phase !== "construct")) throw new BuildError(error.diagnostics.map((d) => ({
2098
+ ...d,
2099
+ phase: "construct"
2100
+ })));
2101
+ throw error;
2102
+ }
2103
+ }
2104
+ type(descriptor) {
2105
+ const info = typeData.get(descriptor);
2106
+ if (!info) this.error("IR_TYPE", "Expected a Builder type descriptor.");
2107
+ if (info.owner && info.owner !== this && (!this.device || definitionOwners.get(this.device) !== info.owner)) this.error("IR_SCOPE", "Named type belongs to another device definition.");
2108
+ this.expectedTypes.add(info);
2109
+ if (info.fields) for (const f of Object.values(info.fields)) this.type(f);
2110
+ return info.type;
2111
+ }
2112
+ sourceFor(options) {
2113
+ const source = options?.source ?? this.source;
2114
+ return source === void 0 ? {} : { source: snapshot(source) };
2115
+ }
2116
+ body(mode, emit, returns, source, setup) {
2117
+ const body = new Body(this, mode, {
2118
+ result: 0,
2119
+ local: 0
2120
+ }, {}, void 0, returns);
2121
+ body.source = source === void 0 ? this.source : snapshot(source);
2122
+ const previous = this.active;
2123
+ this.active = body;
2124
+ try {
2125
+ setup?.(body);
2126
+ emit(body.view());
2127
+ } finally {
2128
+ body.closed = true;
2129
+ this.active = previous;
2130
+ }
2131
+ this.check();
2132
+ return {
2133
+ operations: body.operations,
2134
+ ...body.source ? { source: snapshot(body.source) } : {}
2135
+ };
2136
+ }
2137
+ creation(type, input, mode = "creation", source) {
2138
+ return {
2139
+ type,
2140
+ body: this.body(mode, (view) => {
2141
+ if (typeof input === "function") input(view);
2142
+ else view.returnValue(input);
2143
+ }, type, source)
2144
+ };
2145
+ }
2146
+ declarationType(name) {
2147
+ const declaration = this.names.get(name);
2148
+ return declaration && "type" in declaration ? declaration.type : void 0;
2149
+ }
2150
+ add(declaration) {
2151
+ if (declaration.name !== void 0) {
2152
+ if (this.names.has(declaration.name)) this.error("IR_REFERENCE", `Duplicate declaration ${declaration.name}.`);
2153
+ this.names.set(declaration.name, declaration);
2154
+ }
2155
+ if (declaration.kind === "start" && this.declarations.some((d) => d.kind === "start")) this.error("IR_REFERENCE", "Only one start process is allowed.");
2156
+ this.declarations.push(declaration);
2157
+ }
2158
+ resolve() {
2159
+ for (const pending of this.pending) {
2160
+ const fn = this.names.get(pending.call);
2161
+ if (!fn || fn.kind !== "function") {
2162
+ if (this.jit && pending.requiresSignature) fail("IR_TYPE", "A null or record call argument requires a bound function signature or an explicitly typed Value.");
2163
+ continue;
2164
+ }
2165
+ const parameter = fn.params[pending.index];
2166
+ if (!parameter || parameter.type.kind === "pin" || parameter.type.kind === "memory") this.error("IR_TYPE", "A value argument requires a value parameter.");
2167
+ if (parameter.type.kind === "optional" && pending.value !== null) {
2168
+ const data = pending.body.constantData(parameter.type.inner, pending.value);
2169
+ const id = pending.body.space.result++;
2170
+ const constant = {
2171
+ kind: "constant",
2172
+ result: id,
2173
+ type: parameter.type.inner,
2174
+ constant: data,
2175
+ ...pending.node.source ? { source: pending.node.source } : {}
2176
+ };
2177
+ const insert = (value) => {
2178
+ if (Array.isArray(value)) {
2179
+ const index = value.indexOf(pending.node);
2180
+ if (index >= 0) {
2181
+ value.splice(index, 0, constant);
2182
+ return true;
2183
+ }
2184
+ return value.some(insert);
2185
+ }
2186
+ return value !== null && typeof value === "object" && Object.values(value).some(insert);
2187
+ };
2188
+ if (!insert(this.jit ? this.instructions : this.declarations)) fail("IR_SCHEMA", "Pending call argument is not part of this definition.");
2189
+ delete pending.node.constant;
2190
+ Object.assign(pending.node, {
2191
+ kind: "optional",
2192
+ type: parameter.type,
2193
+ value: id
2194
+ });
2195
+ } else {
2196
+ const data = pending.body.constantData(parameter.type, pending.value);
2197
+ Object.assign(pending.node, {
2198
+ type: parameter.type,
2199
+ constant: data
2200
+ });
2201
+ }
2202
+ }
2203
+ for (const description of this.expectedTypes) {
2204
+ const type = description.type.kind === "optional" ? description.type.inner : description.type;
2205
+ if (!["enum", "record"].includes(type.kind)) continue;
2206
+ const name = type.name;
2207
+ const declaration = this.names.get(name);
2208
+ if (builtins[name] && type.kind === "enum") {
2209
+ if (description.members && (description.members.length !== builtins[name].length || description.members.some((m, i) => m !== builtins[name][i]))) fail("IR_TYPE", "Builtin enum description does not match its members.");
2210
+ continue;
2211
+ }
2212
+ if (!declaration) continue;
2213
+ if (declaration.kind !== type.kind) fail("IR_REFERENCE", "Named type category does not match its declaration.");
2214
+ if (description.members && (description.members.length !== declaration.members.length || description.members.some((m, i) => m !== declaration.members[i]))) fail("IR_TYPE", "Forward enum description does not match its declaration.");
2215
+ if (description.fields) {
2216
+ const expected = Object.entries(description.fields);
2217
+ if (expected.length !== declaration.fields.length || expected.some(([name, t]) => !declaration.fields.some((field) => field.name === name && field.type === typeData.get(t)?.type.kind))) fail("IR_TYPE", "Forward record description does not match its declaration.");
2218
+ }
2219
+ }
2220
+ }
2221
+ };
2222
+ var Body = class Body {
2223
+ root;
2224
+ mode;
2225
+ space;
2226
+ scope;
2227
+ parent;
2228
+ returns;
2229
+ closed = false;
2230
+ ended = false;
2231
+ endedByWaitUntil = false;
2232
+ source;
2233
+ operations = [];
2234
+ facts = /* @__PURE__ */ new Map();
2235
+ localValues = /* @__PURE__ */ new Map();
2236
+ loop = 0;
2237
+ calculate = false;
2238
+ yielded = false;
2239
+ constructor(root, mode, space, scope, parent, returns) {
2240
+ this.root = root;
2241
+ this.mode = mode;
2242
+ this.space = space;
2243
+ this.scope = scope;
2244
+ this.parent = parent;
2245
+ this.returns = returns;
2246
+ }
2247
+ view() {
2248
+ return new Proxy(Object.create(null), { get: (_target, key) => typeof key === "string" ? (...args) => this.root.guard(() => {
2249
+ if (this.closed) this.root.error("IR_LIFECYCLE", "This body Builder is closed.");
2250
+ if (this.root.active !== this) this.root.error("IR_SCOPE", "A captured outer Builder cannot emit into a nested callback.");
2251
+ if (this.ended) this.root.error(this.endedByWaitUntil ? "IR_JIT_BOUNDARY" : "IR_CONTROL_FLOW", "Cannot append an operation after a path terminator.");
2252
+ return this.invoke(key, args);
2253
+ }) : void 0 });
2254
+ }
2255
+ node(node) {
2256
+ const withSource = {
2257
+ ...node,
2258
+ ...node.source === void 0 && this.source !== void 0 ? { source: snapshot(this.source) } : {}
2259
+ };
2260
+ operation(withSource, []);
2261
+ this.operations.push(withSource);
2262
+ }
2263
+ result(type, node, extra = {}) {
2264
+ const id = this.space.result++;
2265
+ this.node({
2266
+ ...node,
2267
+ result: id,
2268
+ type
2269
+ });
2270
+ return fresh(values, {
2271
+ root: this.root,
2272
+ scope: this.scope,
2273
+ space: this.space,
2274
+ type,
2275
+ id,
2276
+ ...extra
2277
+ });
2278
+ }
2279
+ handle(value, expected) {
2280
+ const info = infoOf(value);
2281
+ if (!info || info.root !== this.root || info.space !== this.space || !descendant(this.scope, info.scope)) this.root.error("IR_SCOPE", "Value is not visible in the current execution body or scope.");
2282
+ if (expected && !sameType(info.type, expected)) this.root.error("IR_TYPE", "Operand type does not match the expected type.");
2283
+ return info;
2284
+ }
2285
+ localHandle(value) {
2286
+ const info = locals.get(value);
2287
+ if (!info || info.root !== this.root || info.space !== this.space || !descendant(this.scope, info.scope)) this.root.error("IR_SCOPE", "Local is not visible in this execution body or scope.");
2288
+ return info;
2289
+ }
2290
+ infer(value) {
2291
+ if (infoOf(value)) return this.handle(value).type;
2292
+ const member = enumOf(value);
2293
+ if (member) {
2294
+ if (member.owner && member.owner !== this.root && (!this.root.device || definitionOwners.get(this.root.device) !== member.owner)) this.root.error("IR_SCOPE", "Enum member belongs to another device definition.");
2295
+ return {
2296
+ kind: "enum",
2297
+ name: member.name
2298
+ };
2299
+ }
2300
+ if (typeof value === "number") return n;
2301
+ if (typeof value === "boolean") return b;
2302
+ if (typeof value === "string") return str;
2303
+ if (Array.isArray(value) || value instanceof Uint8Array) return bytes;
2304
+ this.root.error("IR_TYPE", "A null or record literal requires an explicit target type.");
2305
+ }
2306
+ constantData(type, value) {
2307
+ if (type.kind === "optional") {
2308
+ if (value !== null) this.root.error("IR_TYPE", "Optional constants must be lifted from a non-null value.");
2309
+ return { kind: "none" };
2310
+ }
2311
+ if (type.kind === "number" || type.kind === "bool" || type.kind === "string") {
2312
+ if (typeof value !== (type.kind === "bool" ? "boolean" : type.kind)) this.root.error("IR_TYPE", "Constant value does not match its target type.");
2313
+ return {
2314
+ kind: type.kind,
2315
+ value
2316
+ };
2317
+ }
2318
+ if (type.kind === "enum") {
2319
+ const member = enumOf(value);
2320
+ if (!member || member.name !== type.name) this.root.error("IR_TYPE", "Enum constants require a matching enum member token.");
2321
+ if (member.owner && member.owner !== this.root && (!this.root.device || definitionOwners.get(this.root.device) !== member.owner)) this.root.error("IR_SCOPE", "Enum member belongs to another definition.");
2322
+ return {
2323
+ kind: "enum",
2324
+ name: member.name,
2325
+ member: member.member
2326
+ };
2327
+ }
2328
+ if (type.kind === "record") {
2329
+ if (value === null || typeof value !== "object" || Array.isArray(value)) this.root.error("IR_TYPE", "A record constant requires a field object.");
2330
+ const info = [...this.root.expectedTypes].find((info) => {
2331
+ const base = info.type.kind === "optional" ? info.type.inner : info.type;
2332
+ return base.kind === "record" && base.name === type.name && info.fields;
2333
+ });
2334
+ const declaration = this.root.names.get(type.name);
2335
+ const expected = info?.fields ? Object.entries(info.fields).map(([name, t]) => ({
2336
+ name,
2337
+ type: typeData.get(t).type.kind
2338
+ })) : declaration?.kind === "record" ? declaration.fields : void 0;
2339
+ if (!expected) this.root.error("IR_TYPE", "Record constants require a complete named type descriptor.");
2340
+ const input = value;
2341
+ if (Object.keys(input).length !== expected.length || expected.some((field) => !Object.hasOwn(input, field.name) || typeof input[field.name] !== (field.type === "bool" ? "boolean" : "number"))) this.root.error("IR_TYPE", "Record constant fields do not match the target type.");
2342
+ return {
2343
+ kind: "record",
2344
+ name: type.name,
2345
+ fields: Object.keys(input).map((name) => ({
2346
+ name,
2347
+ value: input[name]
2348
+ }))
2349
+ };
2350
+ }
2351
+ if (type.kind === "bytes") {
2352
+ if (!(value instanceof Uint8Array) && !Array.isArray(value)) this.root.error("IR_TYPE", "bytes constants require a Uint8Array or number array.");
2353
+ const array = Array.from(value);
2354
+ if (array.some((element) => !Number.isInteger(element) || element < 0 || element > 255)) this.root.error("IR_TYPE", "Every bytes element must be an integer from zero to 255.");
2355
+ return {
2356
+ kind: "bytes",
2357
+ value: Object.freeze(array)
2358
+ };
2359
+ }
2360
+ this.root.error("IR_TYPE", "sample and intent cannot be constructed from host constants.");
2361
+ }
2362
+ operand(value, expected) {
2363
+ if (infoOf(value)) {
2364
+ const info = this.handle(value);
2365
+ if (expected && expected.kind === "optional" && sameType(info.type, expected.inner)) {
2366
+ const lifted = this.handle(this.result(expected, {
2367
+ kind: "optional",
2368
+ value: info.id
2369
+ }));
2370
+ this.facts.set(alias(lifted), true);
2371
+ return lifted;
2372
+ }
2373
+ if (expected && !sameType(info.type, expected)) this.root.error("IR_TYPE", "Operand type does not match the expected type.");
2374
+ return info;
2375
+ }
2376
+ const type = expected ?? this.infer(value);
2377
+ if (type.kind === "optional" && value !== null) {
2378
+ const constant = this.operand(value, type.inner);
2379
+ const lifted = this.handle(this.result(type, {
2380
+ kind: "optional",
2381
+ value: constant.id
2382
+ }));
2383
+ this.facts.set(alias(lifted), true);
2384
+ return lifted;
2385
+ }
2386
+ return this.handle(this.result(type, {
2387
+ kind: "constant",
2388
+ constant: this.constantData(type, value)
2389
+ }, { none: value === null }));
2390
+ }
2391
+ ref(value, category) {
2392
+ if (typeof value === "string") return {
2393
+ kind: category,
2394
+ name: value
2395
+ };
2396
+ const info = referenceOf(value);
2397
+ if (!info || info.root !== this.root || info.space !== this.space || !descendant(this.scope, info.scope)) this.root.error("IR_SCOPE", "Resource reference is not visible in this body.");
2398
+ if ((info.ref.kind === "parameter" ? info.ref.resource : info.ref.kind) !== category) this.root.error("IR_REFERENCE", "Resource reference category does not match.");
2399
+ return info.ref;
2400
+ }
2401
+ refHandle(name, category) {
2402
+ return fresh(refs, {
2403
+ root: this.root,
2404
+ scope: this.scope,
2405
+ space: this.space,
2406
+ ref: {
2407
+ kind: category,
2408
+ name
2409
+ }
2410
+ });
2411
+ }
2412
+ requireRead() {
2413
+ if (["creation", "initial"].includes(this.mode)) this.root.error("IR_EFFECT", "Runtime resource reads are not available in creation code.");
2414
+ }
2415
+ requireEffect() {
2416
+ if ([
2417
+ "creation",
2418
+ "initial",
2419
+ "pure"
2420
+ ].includes(this.mode)) this.root.error("IR_EFFECT", "Runtime effects are not available in pure or creation code.");
2421
+ }
2422
+ branch(emit, condition, compute, loop = false) {
2423
+ if (typeof emit !== "function") this.root.error("IR_SCHEMA", "A body callback is required.");
2424
+ const body = new Body(this.root, this.mode, this.space, { parent: this.scope }, this, this.returns);
2425
+ body.source = this.source;
2426
+ body.loop = compute ? 0 : this.loop + (loop ? 1 : 0);
2427
+ body.calculate = !!compute || this.calculate;
2428
+ body.facts = new Map(this.facts);
2429
+ body.localValues = new Map(this.localValues);
2430
+ if (condition) for (const [value, fact] of (condition.truth ? condition.info.yes : condition.info.no) ?? []) body.facts.set(alias(value), fact);
2431
+ const previous = this.root.active;
2432
+ this.root.active = body;
2433
+ let returned;
2434
+ try {
2435
+ returned = emit(body.view());
2436
+ if (compute) {
2437
+ if (returned === void 0) {
2438
+ if (!body.ended) this.root.error("IR_CONTROL_FLOW", "A compute callback must produce a value on every normal path.");
2439
+ } else if (!body.ended) {
2440
+ const value = body.operand(returned, compute);
2441
+ body.node({
2442
+ kind: "yield",
2443
+ value: value.id
2444
+ });
2445
+ body.yielded = true;
2446
+ body.ended = true;
2447
+ }
2448
+ }
2449
+ } finally {
2450
+ body.closed = true;
2451
+ this.root.active = previous;
2452
+ }
2453
+ this.root.check();
2454
+ return {
2455
+ body: {
2456
+ operations: body.operations,
2457
+ ...body.source ? { source: snapshot(body.source) } : {}
2458
+ },
2459
+ builder: body,
2460
+ returned
2461
+ };
2462
+ }
2463
+ merge(branches) {
2464
+ const live = branches.filter((branch) => !branch.ended);
2465
+ if (!live.length) {
2466
+ this.ended = true;
2467
+ this.endedByWaitUntil = branches.some((branch) => branch.endedByWaitUntil);
2468
+ return;
2469
+ }
2470
+ this.facts = joinFacts(live.map((branch) => branch.facts));
2471
+ for (const [local, old] of this.localValues) {
2472
+ const candidates = live.map((branch) => branch.localValues.get(local) ?? old);
2473
+ if (candidates.every((value) => value === candidates[0])) this.localValues.set(local, candidates[0]);
2474
+ else {
2475
+ const merged = {
2476
+ ...old,
2477
+ alias: void 0
2478
+ };
2479
+ this.localValues.set(local, merged);
2480
+ if (candidates.every((value, i) => live[i].facts.get(alias(value)) === true)) this.facts.set(merged, true);
2481
+ }
2482
+ }
2483
+ }
2484
+ mergeComputations(branches, includeCurrent = false) {
2485
+ const live = branches.filter((branch) => branch.yielded);
2486
+ if (includeCurrent) live.push(this);
2487
+ if (!live.length) {
2488
+ this.ended = true;
2489
+ return;
2490
+ }
2491
+ this.facts = joinFacts(live.map((branch) => branch.facts));
2492
+ for (const [local, old] of this.localValues) {
2493
+ const candidates = live.map((branch) => branch.localValues.get(local) ?? old);
2494
+ if (candidates.every((value) => value === candidates[0])) this.localValues.set(local, candidates[0]);
2495
+ else {
2496
+ const unknown = {
2497
+ ...old,
2498
+ alias: void 0
2499
+ };
2500
+ this.localValues.set(local, unknown);
2501
+ if (candidates.every((value, i) => live[i].facts.get(alias(value)) === true)) this.facts.set(unknown, true);
2502
+ }
2503
+ }
2504
+ }
2505
+ invoke(method, args) {
2506
+ if (unary.includes(method)) {
2507
+ const value = this.operand(args[0], method === "not" ? b : n);
2508
+ return this.result(method === "not" ? b : n, {
2509
+ kind: "unary",
2510
+ operator: method,
2511
+ value: value.id
2512
+ }, method === "not" ? {
2513
+ yes: value.no ?? /* @__PURE__ */ new Map(),
2514
+ no: value.yes ?? /* @__PURE__ */ new Map()
2515
+ } : {});
2516
+ }
2517
+ if (binary.includes(method)) {
2518
+ const equality = ["equal", "notEqual"].includes(method);
2519
+ const type = equality ? this.root.type(args.shift()) : method === "concat" ? str : n;
2520
+ const left = this.operand(args[0], type), right = this.operand(args[1], type);
2521
+ if (equality && (type.kind === "intent" || type.kind === "optional" && type.inner.kind === "intent" && !left.none && !right.none)) this.root.error("IR_TYPE", "Intent values do not support content equality.");
2522
+ const tested = left.none ? right : right.none ? left : void 0;
2523
+ const comparison = equality || [
2524
+ "lessThan",
2525
+ "lessThanOrEqual",
2526
+ "greaterThan",
2527
+ "greaterThanOrEqual"
2528
+ ].includes(method);
2529
+ return this.result(comparison ? b : type, {
2530
+ kind: "binary",
2531
+ operator: method,
2532
+ left: left.id,
2533
+ right: right.id
2534
+ }, tested ? {
2535
+ yes: /* @__PURE__ */ new Map([[alias(tested), method !== "equal"]]),
2536
+ no: /* @__PURE__ */ new Map([[alias(tested), method === "equal"]])
2537
+ } : {});
2538
+ }
2539
+ if (numeric.includes(method)) {
2540
+ const count = [
2541
+ "min",
2542
+ "max",
2543
+ "pow"
2544
+ ].includes(method) ? 2 : 1;
2545
+ if (args.length !== count) this.root.error("IR_TYPE", "Numeric builtin argument count does not match.");
2546
+ return this.result(method.startsWith("is") ? b : n, {
2547
+ kind: "numeric",
2548
+ builtin: method,
2549
+ args: args.map((value) => this.operand(value, n).id)
2550
+ });
2551
+ }
2552
+ if (loads.includes(method) || stores.includes(method)) {
2553
+ this.requireRead();
2554
+ const memory = this.ref(args[0], "memory"), offset = this.operand(args[1], n);
2555
+ if (stores.includes(method)) {
2556
+ this.requireEffect();
2557
+ this.node({
2558
+ kind: method,
2559
+ memory,
2560
+ offset: offset.id,
2561
+ value: this.operand(args[2], n).id
2562
+ });
2563
+ return;
2564
+ }
2565
+ return this.result(n, {
2566
+ kind: method,
2567
+ memory,
2568
+ offset: offset.id
2569
+ });
2570
+ }
2571
+ switch (method) {
2572
+ case "withSource": {
2573
+ const [source, emit] = args;
2574
+ const view = new Body(this.root, this.mode, this.space, this.scope, this.parent, this.returns);
2575
+ view.source = snapshot(source);
2576
+ view.loop = this.loop;
2577
+ view.calculate = this.calculate;
2578
+ view.facts = this.facts;
2579
+ view.localValues = this.localValues;
2580
+ const previous = this.root.active;
2581
+ this.root.active = view;
2582
+ try {
2583
+ emit(view.view());
2584
+ } finally {
2585
+ view.closed = true;
2586
+ this.root.active = previous;
2587
+ }
2588
+ this.operations.push(...view.operations);
2589
+ this.ended = view.ended;
2590
+ this.endedByWaitUntil = view.endedByWaitUntil;
2591
+ this.facts = view.facts;
2592
+ this.localValues = view.localValues;
2593
+ return;
2594
+ }
2595
+ case "constant":
2596
+ if (infoOf(args[1])) this.root.error("IR_TYPE", "constant requires a host constant, not a Value handle.");
2597
+ return this.objectOperand(args[1], this.root.type(args[0]));
2598
+ case "readProperty":
2599
+ case "readConst":
2600
+ case "readState":
2601
+ case "readDerived": {
2602
+ if (["readState", "readDerived"].includes(method)) this.requireRead();
2603
+ const resource = method.slice(4).toLowerCase();
2604
+ return this.result(this.root.type(args[1]), {
2605
+ kind: "read",
2606
+ resource,
2607
+ name: args[0]
2608
+ });
2609
+ }
2610
+ case "local": {
2611
+ const type = this.root.type(args[0]), initial = this.operand(args[1], type), id = this.space.local++;
2612
+ this.node({
2613
+ kind: "local",
2614
+ local: id,
2615
+ type,
2616
+ initial: initial.id
2617
+ });
2618
+ const data = {
2619
+ root: this.root,
2620
+ space: this.space,
2621
+ scope: this.scope,
2622
+ type,
2623
+ id
2624
+ };
2625
+ this.localValues.set(data, initial);
2626
+ return fresh(locals, data);
2627
+ }
2628
+ case "getLocal": {
2629
+ const local = this.localHandle(args[0]), current = this.localValues.get(local);
2630
+ return this.result(local.type, {
2631
+ kind: "localRead",
2632
+ local: local.id
2633
+ }, {
2634
+ alias: alias(current),
2635
+ yes: current.yes ?? /* @__PURE__ */ new Map(),
2636
+ no: current.no ?? /* @__PURE__ */ new Map(),
2637
+ none: current.none ?? false
2638
+ });
2639
+ }
2640
+ case "setLocal": {
2641
+ const local = this.localHandle(args[0]), value = this.operand(args[1], local.type);
2642
+ this.node({
2643
+ kind: "localWrite",
2644
+ local: local.id,
2645
+ value: value.id
2646
+ });
2647
+ this.localValues.set(local, value);
2648
+ return;
2649
+ }
2650
+ case "record": {
2651
+ const type = this.root.type(args[0]), descriptor = typeData.get(args[0]);
2652
+ const input = args[1];
2653
+ if (type.kind !== "record" || !descriptor.fields || !input || typeof input !== "object" || Object.keys(input).length !== Object.keys(descriptor.fields).length || Object.keys(descriptor.fields).some((key) => !Object.hasOwn(input, key))) this.root.error("IR_TYPE", "Record fields must exactly match the descriptor.");
2654
+ return this.result(type, {
2655
+ kind: "record",
2656
+ fields: Object.keys(input).map((name) => ({
2657
+ name,
2658
+ value: this.operand(input[name], this.root.type(descriptor.fields[name])).id
2659
+ }))
2660
+ });
2661
+ }
2662
+ case "field": {
2663
+ const value = this.handle(args[0]), field = args[1];
2664
+ let type;
2665
+ if (value.type.kind === "sample") type = {
2666
+ voltage: {
2667
+ kind: "optional",
2668
+ inner: n
2669
+ },
2670
+ level: {
2671
+ kind: "enum",
2672
+ name: "Logic"
2673
+ },
2674
+ status: {
2675
+ kind: "enum",
2676
+ name: "SampleStatus"
2677
+ }
2678
+ }[field];
2679
+ else if (value.type.kind === "record") {
2680
+ const name = value.type.name;
2681
+ const f = [...this.root.expectedTypes].find((info) => {
2682
+ const base = info.type.kind === "optional" ? info.type.inner : info.type;
2683
+ return base.kind === "record" && base.name === name && info.fields;
2684
+ })?.fields?.[field];
2685
+ if (f) type = this.root.type(f);
2686
+ else {
2687
+ const declaration = this.root.names.get(name);
2688
+ const found = declaration?.kind === "record" ? declaration.fields.find((f) => f.name === field) : void 0;
2689
+ if (found) type = { kind: found.type };
2690
+ }
2691
+ }
2692
+ if (!type) this.root.error("IR_REFERENCE", "Unknown record or sample field.");
2693
+ const parent = alias(value);
2694
+ parent.fields ??= /* @__PURE__ */ new Map();
2695
+ const previous = parent.fields.get(field);
2696
+ const output = this.result(type, {
2697
+ kind: "field",
2698
+ value: value.id,
2699
+ field
2700
+ }, previous ? { alias: previous } : {});
2701
+ if (!previous) parent.fields.set(field, this.handle(output));
2702
+ return output;
2703
+ }
2704
+ case "bytes": return this.result(bytes, {
2705
+ kind: "bytes",
2706
+ elements: args.map((value) => this.operand(value, n).id)
2707
+ });
2708
+ case "length": {
2709
+ const value = this.handle(args[0]);
2710
+ if (!["string", "bytes"].includes(value.type.kind)) this.root.error("IR_TYPE", "length requires string or bytes.");
2711
+ return this.result(n, {
2712
+ kind: "length",
2713
+ value: value.id
2714
+ });
2715
+ }
2716
+ case "index": {
2717
+ const value = this.handle(args[0]);
2718
+ if (!["string", "bytes"].includes(value.type.kind)) this.root.error("IR_TYPE", "index requires string or bytes.");
2719
+ return this.result(value.type.kind === "string" ? str : n, {
2720
+ kind: "index",
2721
+ value: value.id,
2722
+ index: this.operand(args[1], n).id
2723
+ });
2724
+ }
2725
+ case "optional": {
2726
+ const value = this.handle(args[0]);
2727
+ if (value.type.kind === "optional") this.root.error("IR_TYPE", "Optional types cannot be nested.");
2728
+ const output = this.result({
2729
+ kind: "optional",
2730
+ inner: value.type
2731
+ }, {
2732
+ kind: "optional",
2733
+ value: value.id
2734
+ });
2735
+ this.facts.set(alias(this.handle(output)), true);
2736
+ return output;
2737
+ }
2738
+ case "isNone": {
2739
+ const value = this.handle(args[0]);
2740
+ if (value.type.kind !== "optional") this.root.error("IR_TYPE", "isNone requires an optional value.");
2741
+ return this.result(b, {
2742
+ kind: "isNone",
2743
+ value: value.id
2744
+ }, {
2745
+ yes: /* @__PURE__ */ new Map([[alias(value), false]]),
2746
+ no: /* @__PURE__ */ new Map([[alias(value), true]])
2747
+ });
2748
+ }
2749
+ case "nonNull": {
2750
+ const value = this.handle(args[0]);
2751
+ if (value.type.kind !== "optional" || this.facts.get(alias(value)) !== true) this.root.error("IR_TYPE", "nonNull requires a dominating non-null proof.");
2752
+ return this.result(value.type.inner, {
2753
+ kind: "nonNull",
2754
+ value: value.id
2755
+ });
2756
+ }
2757
+ case "pin":
2758
+ if (this.mode === "creation") this.root.error("IR_EFFECT", "Pin references are not available in ordinary creation computations.");
2759
+ return this.refHandle(args[0], "pin");
2760
+ case "memory":
2761
+ this.requireRead();
2762
+ return this.refHandle(args[0], "memory");
2763
+ case "memoryLength":
2764
+ this.requireRead();
2765
+ return this.result(n, {
2766
+ kind: "memoryLength",
2767
+ memory: this.ref(args[0], "memory")
2768
+ });
2769
+ case "copyBytes":
2770
+ this.requireRead();
2771
+ return this.result(bytes, {
2772
+ kind: "copyBytes",
2773
+ memory: this.ref(args[0], "memory"),
2774
+ offset: this.operand(args[1], n).id,
2775
+ length: this.operand(args[2], n).id
2776
+ });
2777
+ case "readSample":
2778
+ this.requireRead();
2779
+ return this.result(sample, {
2780
+ kind: "readSample",
2781
+ pin: this.ref(args[0], "pin")
2782
+ });
2783
+ case "resistor":
2784
+ if (this.mode === "creation") this.root.error("IR_EFFECT", "Intent construction requires initial-intent or runtime code.");
2785
+ return this.result(intent, {
2786
+ kind: "resistor",
2787
+ to: this.ref(args[0], "pin"),
2788
+ resistance: this.operand(args[1], n).id
2789
+ });
2790
+ case "combineIntents":
2791
+ if (this.mode === "creation") this.root.error("IR_EFFECT", "Intent construction is not available here.");
2792
+ return this.result(intent, {
2793
+ kind: "combineIntents",
2794
+ values: args.map((value) => this.operand(value, intent).id)
2795
+ });
2796
+ case "refEqual": {
2797
+ this.requireRead();
2798
+ const left = referenceOf(args[0]), right = referenceOf(args[1]);
2799
+ if (!left || !right) this.root.error("IR_TYPE", "refEqual requires resource reference handles.");
2800
+ const category = left.ref.kind === "parameter" ? left.ref.resource : left.ref.kind;
2801
+ return this.result(b, {
2802
+ kind: "refEqual",
2803
+ left: this.ref(args[0], category),
2804
+ right: this.ref(args[1], category)
2805
+ });
2806
+ }
2807
+ case "writeState":
2808
+ this.requireEffect();
2809
+ this.node({
2810
+ kind: "write",
2811
+ resource: "state",
2812
+ name: args[0],
2813
+ value: this.operand(args[1], this.root.declarationType(args[0])).id
2814
+ });
2815
+ return;
2816
+ case "writeIntent":
2817
+ this.requireEffect();
2818
+ this.node({
2819
+ kind: "writeIntent",
2820
+ pin: this.ref(args[0], "pin"),
2821
+ value: this.operand(args[1], intent).id
2822
+ });
2823
+ return;
2824
+ case "send":
2825
+ this.requireEffect();
2826
+ this.node({
2827
+ kind: "send",
2828
+ channel: args[0],
2829
+ value: this.operand(args[1], this.root.declarationType(args[0])).id
2830
+ });
2831
+ return;
2832
+ case "notify":
2833
+ this.requireEffect();
2834
+ this.node({
2835
+ kind: "notify",
2836
+ notification: args[0]
2837
+ });
2838
+ return;
2839
+ case "now":
2840
+ case "random":
2841
+ this.requireEffect();
2842
+ return this.result(n, { kind: method });
2843
+ case "call":
2844
+ case "callValue":
2845
+ case "callPure":
2846
+ case "callPureValue": {
2847
+ const pure = method.startsWith("callPure"), hasValue = method.endsWith("Value");
2848
+ if (!pure) this.requireEffect();
2849
+ const name = args.shift(), type = hasValue ? this.root.type(args.shift()) : void 0;
2850
+ const signature = this.root.names.get(name);
2851
+ const parameters = signature?.kind === "function" ? signature.params : void 0;
2852
+ const argumentsIR = args.map((value, index) => {
2853
+ const ref = referenceOf(value);
2854
+ if (ref) {
2855
+ if (this.mode === "creation") this.root.error("IR_EFFECT", "Resource arguments are not available in ordinary creation code.");
2856
+ return this.ref(value, ref.ref.kind === "parameter" ? ref.ref.resource : ref.ref.kind);
2857
+ }
2858
+ const expected = parameters?.[index]?.type;
2859
+ if (expected && ["pin", "memory"].includes(expected.kind)) this.root.error("IR_TYPE", "A resource reference argument is required.");
2860
+ if (!expected && !infoOf(value)) {
2861
+ const requiresSignature = value === null || typeof value === "object" && !enumOf(value) && !Array.isArray(value) && !(value instanceof Uint8Array);
2862
+ const inferred = requiresSignature ? n : this.infer(value);
2863
+ const node = {
2864
+ kind: "constant",
2865
+ result: this.space.result++,
2866
+ type: inferred,
2867
+ constant: requiresSignature ? {
2868
+ kind: "number",
2869
+ value: 0
2870
+ } : this.constantData(inferred, value),
2871
+ ...this.source ? { source: snapshot(this.source) } : {}
2872
+ };
2873
+ this.operations.push(node);
2874
+ this.root.pending.push({
2875
+ node,
2876
+ call: name,
2877
+ index,
2878
+ value: enumOf(value) ? value : snapshot(value),
2879
+ body: this,
2880
+ requiresSignature
2881
+ });
2882
+ return {
2883
+ kind: "value",
2884
+ value: node.result
2885
+ };
2886
+ }
2887
+ return {
2888
+ kind: "value",
2889
+ value: this.operand(value, expected).id
2890
+ };
2891
+ });
2892
+ const node = {
2893
+ kind: hasValue ? "callValue" : "call",
2894
+ name,
2895
+ pure,
2896
+ args: argumentsIR
2897
+ };
2898
+ if (hasValue) return this.result(type, node);
2899
+ this.node(node);
2900
+ return;
2901
+ }
2902
+ case "receive":
2903
+ if (this.calculate || this.mode !== "process") this.root.error("IR_EFFECT", "receive requires a process statement region.");
2904
+ return this.result(this.root.type(args[1]), {
2905
+ kind: "receive",
2906
+ channel: args[0]
2907
+ });
2908
+ case "if": {
2909
+ const condition = this.operand(args[0], b);
2910
+ const yes = this.branch(args[1], {
2911
+ info: condition,
2912
+ truth: true
2913
+ });
2914
+ const no = this.branch(args[2] ?? (() => {}), {
2915
+ info: condition,
2916
+ truth: false
2917
+ });
2918
+ this.node({
2919
+ kind: "if",
2920
+ condition: condition.id,
2921
+ yes: yes.body,
2922
+ ...args[2] ? { no: no.body } : {}
2923
+ });
2924
+ this.merge([yes.builder, no.builder]);
2925
+ return;
2926
+ }
2927
+ case "match": {
2928
+ const value = this.handle(args[0]), arms = args[1];
2929
+ if (!Array.isArray(arms)) this.root.error("IR_SCHEMA", "match arms must be an array.");
2930
+ const builders = [];
2931
+ const nodes = arms.map((arm) => {
2932
+ const pattern = arm.pattern === null ? { kind: "none" } : this.constantData(value.type.kind === "optional" ? value.type.inner : value.type, arm.pattern);
2933
+ if (![
2934
+ "none",
2935
+ "number",
2936
+ "bool",
2937
+ "string",
2938
+ "enum"
2939
+ ].includes(pattern.kind)) this.root.error("IR_TYPE", "Invalid match pattern.");
2940
+ const condition = {
2941
+ ...value,
2942
+ yes: /* @__PURE__ */ new Map([[alias(value), pattern.kind !== "none"]])
2943
+ };
2944
+ const branch = this.branch(arm.emit, {
2945
+ info: condition,
2946
+ truth: true
2947
+ });
2948
+ builders.push(branch.builder);
2949
+ return {
2950
+ pattern,
2951
+ body: branch.body
2952
+ };
2953
+ });
2954
+ const defaultCondition = nodes.some((node) => node.pattern.kind === "none") ? {
2955
+ info: {
2956
+ ...value,
2957
+ yes: /* @__PURE__ */ new Map([[alias(value), true]])
2958
+ },
2959
+ truth: true
2960
+ } : void 0;
2961
+ const fallback = args[2] ? this.branch(args[2], defaultCondition) : void 0;
2962
+ if (fallback) builders.push(fallback.builder);
2963
+ this.node({
2964
+ kind: "match",
2965
+ value: value.id,
2966
+ arms: nodes,
2967
+ ...fallback ? { otherwise: fallback.body } : {}
2968
+ });
2969
+ this.merge(builders.length ? builders : [this]);
2970
+ return;
2971
+ }
2972
+ case "while":
2973
+ case "loop": {
2974
+ const before = new Map(this.localValues);
2975
+ const test = method === "while" ? this.branch(args[0], void 0, b) : void 0;
2976
+ const body = this.branch(args[method === "while" ? 1 : 0], void 0, void 0, true);
2977
+ this.node({
2978
+ kind: method,
2979
+ ...test ? { test: {
2980
+ type: b,
2981
+ body: test.body
2982
+ } } : {},
2983
+ body: body.body
2984
+ });
2985
+ for (const [local, old] of before) if (body.builder.localValues.get(local) !== old || test && test.builder.localValues.get(local) !== old) this.localValues.set(local, {
2986
+ ...old,
2987
+ alias: void 0
2988
+ });
2989
+ if (test && !test.builder.yielded) this.ended = true;
2990
+ return;
2991
+ }
2992
+ case "and":
2993
+ case "or": {
2994
+ const left = this.operand(args[0], b), isAnd = method === "and";
2995
+ const branch = this.branch(args[1], {
2996
+ info: left,
2997
+ truth: isAnd
2998
+ }, b);
2999
+ const right = infoOf(branch.returned);
3000
+ const combine = (a, b) => new Map([...a ?? [], ...b ?? []]);
3001
+ const yes = isAnd ? combine(left.yes, right?.yes) : joinFacts([left.yes ?? /* @__PURE__ */ new Map(), combine(left.no, right?.yes)]);
3002
+ const no = isAnd ? joinFacts([left.no ?? /* @__PURE__ */ new Map(), combine(left.yes, right?.no)]) : combine(left.no, right?.no);
3003
+ const value = this.result(b, {
3004
+ kind: method,
3005
+ left: left.id,
3006
+ right: {
3007
+ type: b,
3008
+ body: branch.body
3009
+ }
3010
+ }, {
3011
+ yes,
3012
+ no
3013
+ });
3014
+ this.mergeComputations([branch.builder], true);
3015
+ return value;
3016
+ }
3017
+ case "select": {
3018
+ const type = this.root.type(args[0]), condition = this.operand(args[1], b);
3019
+ const yes = this.branch(args[2], {
3020
+ info: condition,
3021
+ truth: true
3022
+ }, type), no = this.branch(args[3], {
3023
+ info: condition,
3024
+ truth: false
3025
+ }, type);
3026
+ const value = this.result(type, {
3027
+ kind: "select",
3028
+ condition: condition.id,
3029
+ yes: {
3030
+ type,
3031
+ body: yes.body
3032
+ },
3033
+ no: {
3034
+ type,
3035
+ body: no.body
3036
+ }
3037
+ });
3038
+ this.mergeComputations([yes.builder, no.builder]);
3039
+ return value;
3040
+ }
3041
+ case "assert": {
3042
+ const condition = this.operand(args[0], b), message = args[1] ? this.branch(args[1], {
3043
+ info: condition,
3044
+ truth: false
3045
+ }, str) : void 0;
3046
+ this.node({
3047
+ kind: "assert",
3048
+ condition: condition.id,
3049
+ ...message ? { message: {
3050
+ type: str,
3051
+ body: message.body
3052
+ } } : {}
3053
+ });
3054
+ for (const [value, fact] of condition.yes ?? []) this.facts.set(alias(value), fact);
3055
+ return;
3056
+ }
3057
+ case "wait":
3058
+ case "elapse":
3059
+ if (this.calculate || (method === "wait" ? this.mode !== "process" : this.mode !== "jit")) this.root.error("IR_EFFECT", `${method} is not permitted in this context.`);
3060
+ this.node({
3061
+ kind: method,
3062
+ duration: this.operand(args[0], n).id
3063
+ });
3064
+ return;
3065
+ case "waitUntil":
3066
+ if (this.mode !== "jit" || this.calculate || this.loop) this.root.error("IR_JIT_BOUNDARY", "waitUntil requires a JIT tail path outside loops and computations.");
3067
+ this.node({
3068
+ kind: "waitUntil",
3069
+ predicate: args[0]
3070
+ });
3071
+ this.ended = true;
3072
+ this.endedByWaitUntil = true;
3073
+ return;
3074
+ case "redirect":
3075
+ if (!["jit", "sync"].includes(this.mode)) this.root.error("IR_EFFECT", "redirect requires a JIT-capable context.");
3076
+ this.node({
3077
+ kind: "redirect",
3078
+ target: this.operand(args[0], n).id,
3079
+ delay: this.operand(args[1] ?? 0, n).id
3080
+ });
3081
+ this.ended = true;
3082
+ return;
3083
+ case "stop":
3084
+ if (this.mode !== "jit" || this.calculate) this.root.error("IR_EFFECT", "stop requires a JIT statement region.");
3085
+ this.node({ kind: "stop" });
3086
+ this.ended = true;
3087
+ return;
3088
+ case "break":
3089
+ case "continue":
3090
+ if (!this.loop) this.root.error("IR_CONTROL_FLOW", `${method} requires an enclosing loop.`);
3091
+ this.node({ kind: method });
3092
+ this.ended = true;
3093
+ return;
3094
+ case "return":
3095
+ if (this.calculate || this.returns || [
3096
+ "jit",
3097
+ "creation",
3098
+ "initial"
3099
+ ].includes(this.mode)) this.root.error("IR_CONTROL_FLOW", "Void return is not permitted in this body.");
3100
+ this.node({ kind: "return" });
3101
+ this.ended = true;
3102
+ return;
3103
+ case "returnValue":
3104
+ if (this.calculate || !this.returns) this.root.error("IR_CONTROL_FLOW", "returnValue is not permitted in this body.");
3105
+ this.node({
3106
+ kind: "returnValue",
3107
+ value: this.operand(args[0], this.returns).id
3108
+ });
3109
+ this.ended = true;
3110
+ return;
3111
+ default: this.root.error("IR_SCHEMA", `Unknown Builder operation ${method}.`);
3112
+ }
3113
+ }
3114
+ objectOperand(value, type) {
3115
+ return fresh(values, this.operand(value, type));
3116
+ }
3117
+ };
3118
+ function deviceView(root, temporary = { closed: false }) {
3119
+ return new Proxy(Object.create(null), { get: (_target, method) => typeof method === "string" ? (...args) => {
3120
+ if (method === "finish") {
3121
+ root.check();
3122
+ if (temporary.closed) root.error("IR_LIFECYCLE", "This source view is closed.");
3123
+ if (root.active) root.error("IR_SCOPE", "Cannot finish a device while a body callback is active.");
3124
+ root.closed = true;
3125
+ root.resolve();
3126
+ const definition = validateDeviceIR({
3127
+ kind: "device",
3128
+ name: root.options.name,
3129
+ declarations: root.declarations,
3130
+ ...root.sourceFor()
3131
+ });
3132
+ definitionOwners.set(definition, root);
3133
+ return definition;
3134
+ }
3135
+ return root.guard(() => {
3136
+ if (temporary.closed) root.error("IR_LIFECYCLE", "This source view is closed.");
3137
+ if (root.active) root.error("IR_SCOPE", "Cannot add declarations inside an executable body callback.");
3138
+ if (method === "withSource") {
3139
+ const previous = root.source, viewState = { closed: false };
3140
+ root.source = snapshot(args[0]);
3141
+ try {
3142
+ args[1](deviceView(root, viewState));
3143
+ } finally {
3144
+ viewState.closed = true;
3145
+ root.source = previous;
3146
+ }
3147
+ return;
3148
+ }
3149
+ const options = args[0] ?? {}, source = options.source ?? root.source;
3150
+ const base = {
3151
+ name: options.name,
3152
+ ...root.sourceFor(options)
3153
+ };
3154
+ if (method === "enum") {
3155
+ const descriptor = factory.namedEnum(options.name, options.members);
3156
+ const data = typeData.get(descriptor);
3157
+ data.owner = root;
3158
+ const owned = enumType(options.name, options.members, root);
3159
+ root.add({
3160
+ kind: "enum",
3161
+ ...base,
3162
+ members: [...options.members]
3163
+ });
3164
+ return owned;
3165
+ }
3166
+ if (method === "record") {
3167
+ factory.namedRecord(options.name, options.fields);
3168
+ for (const type of Object.values(options.fields)) root.type(type);
3169
+ const descriptor = recordType(options.name, options.fields, root);
3170
+ root.add({
3171
+ kind: "record",
3172
+ ...base,
3173
+ fields: Object.entries(options.fields).map(([name, type]) => ({
3174
+ name,
3175
+ type: root.type(type).kind
3176
+ }))
3177
+ });
3178
+ return descriptor;
3179
+ }
3180
+ if ([
3181
+ "property",
3182
+ "constant",
3183
+ "state"
3184
+ ].includes(method)) {
3185
+ const type = root.type(options.type), key = method === "property" ? "default" : method === "constant" ? "value" : "initial";
3186
+ if (key !== "default" && !Object.hasOwn(options, key)) root.error("IR_SCHEMA", `Missing ${key}.`);
3187
+ root.add({
3188
+ kind: method === "constant" ? "const" : method,
3189
+ ...base,
3190
+ type,
3191
+ ...Object.hasOwn(options, key) ? { [key]: root.creation(type, options[key], "creation", source) } : {}
3192
+ });
3193
+ return;
3194
+ }
3195
+ if (method === "derived") {
3196
+ const type = root.type(options.type);
3197
+ root.add({
3198
+ kind: "derived",
3199
+ ...base,
3200
+ type,
3201
+ value: {
3202
+ type,
3203
+ body: root.body("pure", args[1], type, source)
3204
+ }
3205
+ });
3206
+ return;
3207
+ }
3208
+ if (method === "pin") {
3209
+ if (Object.hasOwn(options, "low") !== Object.hasOwn(options, "high")) root.error("IR_SCHEMA", "Pin thresholds must be specified together.");
3210
+ root.add({
3211
+ kind: "pin",
3212
+ ...base,
3213
+ ...Object.hasOwn(options, "low") ? {
3214
+ low: root.creation(n, options.low, "creation", source),
3215
+ high: root.creation(n, options.high, "creation", source)
3216
+ } : {},
3217
+ ...Object.hasOwn(options, "initial") ? { initial: root.creation(intent, options.initial, "initial", source) } : {}
3218
+ });
3219
+ return;
3220
+ }
3221
+ if (method === "memory") {
3222
+ root.add({
3223
+ kind: "memory",
3224
+ ...base,
3225
+ public: options.public ?? false,
3226
+ length: root.creation(n, options.length, "creation", source)
3227
+ });
3228
+ return;
3229
+ }
3230
+ if (method === "channel") {
3231
+ const type = root.type(options.type);
3232
+ root.add({
3233
+ kind: "channel",
3234
+ ...base,
3235
+ direction: options.direction,
3236
+ type,
3237
+ capacity: root.creation(n, options.capacity, "creation", source),
3238
+ ...Object.hasOwn(options, "maxLength") ? { maxLength: root.creation(n, options.maxLength, "creation", source) } : {},
3239
+ ...Object.hasOwn(options, "overflow") ? { overflow: root.creation({
3240
+ kind: "enum",
3241
+ name: "Overflow"
3242
+ }, options.overflow, "creation", source) } : {}
3243
+ });
3244
+ return;
3245
+ }
3246
+ if (method === "notification") {
3247
+ root.add({
3248
+ kind: "notification",
3249
+ ...base,
3250
+ direction: options.direction
3251
+ });
3252
+ return;
3253
+ }
3254
+ if (method === "fn" || method === "pureFn") {
3255
+ const pure = method === "pureFn", returns = Object.hasOwn(options, "returns") ? root.type(options.returns) : void 0;
3256
+ if (!Array.isArray(options.params)) root.error("IR_SCHEMA", "Function parameters must be an array.");
3257
+ const params = options.params.map((param) => ({
3258
+ name: param.name,
3259
+ type: param.type === types.pin ? { kind: "pin" } : param.type === types.memory ? { kind: "memory" } : root.type(param.type)
3260
+ }));
3261
+ let bindings = {};
3262
+ const body = root.body(pure ? "pure" : "sync", (view) => args[1](view, bindings), returns, source, (body) => {
3263
+ bindings = Object.create(null);
3264
+ params.forEach((param, index) => {
3265
+ bindings[param.name] = param.type.kind === "pin" || param.type.kind === "memory" ? fresh(refs, {
3266
+ root,
3267
+ scope: body.scope,
3268
+ space: body.space,
3269
+ ref: {
3270
+ kind: "parameter",
3271
+ resource: param.type.kind,
3272
+ index
3273
+ }
3274
+ }) : body.result(param.type, {
3275
+ kind: "parameter",
3276
+ index
3277
+ });
3278
+ });
3279
+ });
3280
+ root.add({
3281
+ kind: "function",
3282
+ ...base,
3283
+ pure,
3284
+ params,
3285
+ ...returns ? { returns } : {},
3286
+ body
3287
+ });
3288
+ return;
3289
+ }
3290
+ if (method === "start") {
3291
+ root.add({
3292
+ kind: "start",
3293
+ body: root.body("process", args[0]),
3294
+ ...root.sourceFor()
3295
+ });
3296
+ return;
3297
+ }
3298
+ if (method === "on") {
3299
+ root.add({
3300
+ kind: "on",
3301
+ ...base,
3302
+ sourcePath: snapshot(options.sourcePath),
3303
+ body: root.body("process", args[1], void 0, source)
3304
+ });
3305
+ return;
3306
+ }
3307
+ if (method === "reaction") {
3308
+ if (!Array.isArray(options.captures) || !args[1] || typeof args[1] !== "object") root.error("IR_SCHEMA", "Reaction captures require descriptors and an emitter object.");
3309
+ const emitters = args[1];
3310
+ const names = options.captures.map((capture) => capture.name);
3311
+ const keys = Object.keys(args[1]);
3312
+ if (keys.length !== names.length || keys.some((key) => !names.includes(key)) || names.some((name) => typeof emitters[name] !== "function")) root.error("IR_SCHEMA", "Reaction capture emitter keys must exactly match descriptor names.");
3313
+ const sourceType = root.type(options.sourceType);
3314
+ const captures = options.captures.map((capture) => {
3315
+ const type = root.type(capture.type);
3316
+ return {
3317
+ name: capture.name,
3318
+ value: {
3319
+ type,
3320
+ body: root.body("pure", emitters[capture.name], type, capture.source ?? source)
3321
+ },
3322
+ ...root.sourceFor(capture)
3323
+ };
3324
+ });
3325
+ let bindings = {};
3326
+ const body = root.body("reaction", (view) => args[2](view, bindings), void 0, source, (body) => {
3327
+ bindings = {
3328
+ previous: body.result(sourceType, {
3329
+ kind: "reactionValue",
3330
+ binding: "previous"
3331
+ }),
3332
+ current: body.result(sourceType, {
3333
+ kind: "reactionValue",
3334
+ binding: "current"
3335
+ }),
3336
+ captures: Object.create(null)
3337
+ };
3338
+ captures.forEach((capture, index) => {
3339
+ bindings.captures[capture.name] = body.result(capture.value.type, {
3340
+ kind: "reactionCapture",
3341
+ index
3342
+ });
3343
+ });
3344
+ });
3345
+ root.add({
3346
+ kind: "reaction",
3347
+ ...base,
3348
+ sourcePath: snapshot(options.sourcePath),
3349
+ sourceType,
3350
+ previous: options.previous,
3351
+ current: options.current,
3352
+ captures,
3353
+ body
3354
+ });
3355
+ return;
3356
+ }
3357
+ if (method === "assert") {
3358
+ root.add({
3359
+ kind: "assert",
3360
+ body: root.body("creation", args[0]),
3361
+ ...root.sourceFor()
3362
+ });
3363
+ return;
3364
+ }
3365
+ root.error("IR_SCHEMA", `Unknown device Builder method ${method}.`);
3366
+ });
3367
+ } : void 0 });
3368
+ }
3369
+ function createDeviceBuilder(options) {
3370
+ if (options === void 0) options = { name: "GeneratedDevice" };
3371
+ if (!options || typeof options.name !== "string") fail("IR_SCHEMA", "A device name is required.", [], "construct");
3372
+ return deviceView(new Root({
3373
+ name: options.name,
3374
+ ...options.source ? { source: snapshot(options.source) } : {}
3375
+ }));
3376
+ }
3377
+ /** Runtime integration factory; only existing resources can be referenced by the returned view. */
3378
+ function createJitBlockBuilder(options = {}) {
3379
+ const root = new Root({
3380
+ name: "JitBlock",
3381
+ ...options.source ? { source: options.source } : {}
3382
+ }, options.device, true);
3383
+ if (options.device) {
3384
+ for (const declaration of options.device.declarations) if ("name" in declaration) root.names.set(declaration.name, declaration);
3385
+ }
3386
+ const view = (state = { closed: false }) => new Proxy(Object.create(null), { get: (_target, method) => typeof method === "string" ? (...args) => {
3387
+ if (method === "finish") {
3388
+ root.check();
3389
+ if (state.closed) root.error("IR_LIFECYCLE", "This source view is closed.");
3390
+ if (root.active) root.error("IR_SCOPE", "Cannot finish a block inside an instruction callback.");
3391
+ root.closed = true;
3392
+ root.resolve();
3393
+ const block = {
3394
+ kind: "block",
3395
+ instructions: root.instructions,
3396
+ ...root.sourceFor()
3397
+ };
3398
+ return options.device ? validateBoundBlockIR(block, options.device) : validateBlockIR(block);
3399
+ }
3400
+ return root.guard(() => {
3401
+ if (state.closed) root.error("IR_LIFECYCLE", "This source view is closed.");
3402
+ if (root.active) root.error("IR_SCOPE", "Cannot add instructions inside an instruction callback.");
3403
+ if (method === "withSource") {
3404
+ const old = root.source, nested = { closed: false };
3405
+ root.source = snapshot(args[0]);
3406
+ try {
3407
+ args[1](view(nested));
3408
+ } finally {
3409
+ nested.closed = true;
3410
+ root.source = old;
3411
+ }
3412
+ return;
3413
+ }
3414
+ if (method !== "instruction") root.error("IR_SCHEMA", `Unknown block Builder method ${method}.`);
3415
+ const boundary = args[0];
3416
+ if (!boundary || !Number.isFinite(boundary.pc) || !Number.isFinite(boundary.nextPc)) root.error("IR_SCHEMA", "Instruction boundaries require finite pc and nextPc.");
3417
+ const previous = root.instructions.at(-1);
3418
+ if (previous && previous.nextPc !== boundary.pc) root.error("IR_JIT_BOUNDARY", "Adjacent instruction boundaries must match.");
3419
+ const body = root.body("jit", args[1], void 0, boundary.source ?? root.source);
3420
+ root.instructions.push({
3421
+ pc: boundary.pc,
3422
+ nextPc: boundary.nextPc,
3423
+ body,
3424
+ ...root.sourceFor(boundary)
3425
+ });
3426
+ });
3427
+ } : void 0 });
3428
+ return view();
3429
+ }
3430
+ //#endregion
3431
+ export { BuildError, createDeviceBuilder, createJitBlockBuilder, types, validateBlockIR, validateBoundBlockIR, validateDeviceIR };
3432
+
3433
+ //# sourceMappingURL=index.js.map