@fraczak/k 7.0.0 → 7.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/DOCS/BIG_PICTURE.md +5 -5
  3. package/DOCS/FILE_FORMATS.md +5 -5
  4. package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
  5. package/DOCS/PATTERNS.md +3 -3
  6. package/DOCS/PROFILING_AND_TRACING.md +60 -72
  7. package/DOCS/REPL.md +3 -3
  8. package/DOCS/book/03-types-as-finite-automata.md +4 -4
  9. package/DOCS/book/06-values-in-memory.md +9 -9
  10. package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
  11. package/DOCS/book/09-compiler-architecture.md +2 -2
  12. package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
  13. package/DOCS/book/14-canonical-serialization.md +1 -1
  14. package/Examples/arithmetics.k +2 -2
  15. package/Examples/bnat.k +1 -1
  16. package/Examples/karatsuba-mult.k +2 -2
  17. package/README.md +3 -8
  18. package/backend-api.mjs +9 -1
  19. package/backends/llvm/runtime/krt.c +33 -0
  20. package/backends/llvm/runtime/krt.h +1 -0
  21. package/backends/llvm/scripts/run-tests.mjs +5 -0
  22. package/backends/llvm/src/executable.mjs +4 -4
  23. package/backends/llvm/src/kvm2llvm.mjs +42 -19
  24. package/backends/llvm/src/llvm.mjs +30 -36
  25. package/backends/llvm/tests/integration.sh +2 -2
  26. package/backends/llvm/tests/perf-support.mjs +1 -55
  27. package/backends/llvm/tests/test-llvm.mjs +3 -3
  28. package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
  29. package/backends/wasm/demo_ieee.mjs +2 -3
  30. package/backends/wasm/runtime.wat +157 -5
  31. package/backends/wasm/src/kvm2wasm.mjs +57 -34
  32. package/backends/wasm/src/wasm.mjs +40 -18
  33. package/backends/wasm/tests/perf-support.mjs +4 -4
  34. package/backends/wasm/tests/test-wasm.mjs +13 -14
  35. package/browser/vfs-data.mjs +6 -6
  36. package/core.k +8 -8
  37. package/harness/format.mjs +2 -2
  38. package/harness/registry.mjs +0 -2
  39. package/kir.mjs +299 -17
  40. package/package.json +1 -5
  41. package/scripts/benchmark-idempotency.mjs +12 -8
  42. package/scripts/benchmark.mjs +5 -9
  43. package/scripts/build-repl-html.mjs +2 -2
  44. package/scripts/perf-ieee.mjs +17 -118
  45. package/scripts/perf-int.mjs +313 -318
  46. package/scripts/perf-multiplication.mjs +10 -20
  47. package/scripts/perf-poly.mjs +29 -183
  48. package/tests/code-derivation/pattern-envelope.mjs +4 -4
  49. package/tests/test-kvm-polymorphic.mjs +2 -2
  50. package/tests/test-repl-html.mjs +15 -0
  51. package/tests/test-repl.mjs +10 -0
  52. package/backends/arm64/README.md +0 -103
  53. package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
  54. package/backends/arm64/bin/k-arm64-run.mjs +0 -58
  55. package/backends/arm64/bin/k-arm64.mjs +0 -88
  56. package/backends/arm64/scripts/run-tests.mjs +0 -57
  57. package/backends/arm64/src/arm64.mjs +0 -229
  58. package/backends/arm64/src/cli.mjs +0 -155
  59. package/backends/arm64/src/kvm2arm64.mjs +0 -906
  60. package/backends/arm64/src/runtime.c +0 -744
  61. package/backends/arm64/tests/integration.sh +0 -112
  62. package/backends/arm64/tests/test-arm64.mjs +0 -240
  63. package/harness/adapters/arm64.mjs +0 -105
@@ -398,6 +398,14 @@
398
398
  i32.ge_u
399
399
  br_if $break_prod
400
400
 
401
+ ;; Copy field tagId at offset 8 + 4*i
402
+ local.get $dest
403
+ i32.const 8
404
+ local.get $i
405
+ i32.const 4
406
+ i32.mul
407
+ i32.add
408
+ i32.add
401
409
  local.get $curr
402
410
  i32.const 8
403
411
  local.get $i
@@ -406,17 +414,19 @@
406
414
  i32.add
407
415
  i32.add
408
416
  i32.load
409
- local.set $offsetVal
417
+ i32.store
410
418
 
411
- local.get $dest
419
+ ;; Value slot offset is 8 + 4*N + 4*i
412
420
  i32.const 8
413
- local.get $i
421
+ local.get $N
414
422
  i32.const 4
415
423
  i32.mul
416
424
  i32.add
425
+ local.get $i
426
+ i32.const 4
427
+ i32.mul
417
428
  i32.add
418
- local.get $offsetVal
419
- i32.store
429
+ local.set $offsetVal
420
430
 
421
431
  local.get $curr
422
432
  local.get $offsetVal
@@ -472,4 +482,146 @@
472
482
  i32.add
473
483
  global.set $arena_free
474
484
  )
485
+
486
+ ;; Lookup product field by tagId with predictedIndex hint
487
+ (func $product_get (export "product_get") (param $prod i32) (param $targetTagId i32) (param $predictedIndex i32) (result i32)
488
+ (local $size i32)
489
+ (local $N i32)
490
+ (local $i i32)
491
+
492
+ ;; 1. Null check
493
+ local.get $prod
494
+ i32.eqz
495
+ if
496
+ i32.const 0
497
+ return
498
+ end
499
+
500
+ ;; 2. Read totalSize at prod + 0
501
+ local.get $prod
502
+ i32.load offset=0
503
+ local.set $size
504
+
505
+ ;; 3. Validate product header (not a variant and size >= 8)
506
+ local.get $size
507
+ i32.const 12
508
+ i32.eq
509
+ if
510
+ i32.const 0
511
+ return
512
+ end
513
+ local.get $size
514
+ i32.const 8
515
+ i32.lt_u
516
+ if
517
+ i32.const 0
518
+ return
519
+ end
520
+
521
+ ;; 4. Read field count N at prod + 4
522
+ local.get $prod
523
+ i32.load offset=4
524
+ local.set $N
525
+
526
+ ;; 5. Validate size == 8 + 8 * N
527
+ local.get $size
528
+ i32.const 8
529
+ local.get $N
530
+ i32.const 8
531
+ i32.mul
532
+ i32.add
533
+ i32.ne
534
+ if
535
+ i32.const 0
536
+ return
537
+ end
538
+
539
+ ;; 6. Fast path: check predictedIndex if in range [0, N)
540
+ local.get $predictedIndex
541
+ i32.const 0
542
+ i32.ge_s
543
+ if
544
+ local.get $predictedIndex
545
+ local.get $N
546
+ i32.lt_u
547
+ if
548
+ ;; Load tagId at prod + 8 + 4 * predictedIndex
549
+ local.get $prod
550
+ i32.const 8
551
+ local.get $predictedIndex
552
+ i32.const 4
553
+ i32.mul
554
+ i32.add
555
+ i32.add
556
+ i32.load
557
+ local.get $targetTagId
558
+ i32.eq
559
+ if
560
+ ;; Match! Return value at prod + 8 + 4*N + 4*predictedIndex
561
+ local.get $prod
562
+ i32.const 8
563
+ local.get $N
564
+ i32.const 4
565
+ i32.mul
566
+ i32.add
567
+ local.get $predictedIndex
568
+ i32.const 4
569
+ i32.mul
570
+ i32.add
571
+ i32.add
572
+ i32.load
573
+ return
574
+ end
575
+ end
576
+ end
577
+
578
+ ;; 7. Scan path: check i = 0 .. N-1
579
+ i32.const 0
580
+ local.set $i
581
+ (block $break_scan
582
+ (loop $loop_scan
583
+ local.get $i
584
+ local.get $N
585
+ i32.ge_u
586
+ br_if $break_scan
587
+
588
+ ;; Load tagId at prod + 8 + 4 * i
589
+ local.get $prod
590
+ i32.const 8
591
+ local.get $i
592
+ i32.const 4
593
+ i32.mul
594
+ i32.add
595
+ i32.add
596
+ i32.load
597
+ local.get $targetTagId
598
+ i32.eq
599
+ if
600
+ ;; Match! Return value at prod + 8 + 4*N + 4*i
601
+ local.get $prod
602
+ i32.const 8
603
+ local.get $N
604
+ i32.const 4
605
+ i32.mul
606
+ i32.add
607
+ local.get $i
608
+ i32.const 4
609
+ i32.mul
610
+ i32.add
611
+ i32.add
612
+ i32.load
613
+ return
614
+ end
615
+
616
+ local.get $i
617
+ i32.const 1
618
+ i32.add
619
+ local.set $i
620
+ br $loop_scan
621
+ )
622
+ )
623
+
624
+ ;; 8. Not found: return 0
625
+ i32.const 0
626
+ )
475
627
  )
@@ -108,22 +108,28 @@ export function lowerToWasm(relDef, name, options = {}) {
108
108
  const getPattern = (rName, inputMap = {}) => registerPatterns.get(mappedReg(rName, inputMap));
109
109
 
110
110
  const getProductEdges = (inst, inputMap) => {
111
- if (typePatternGraph) {
112
- const inputPatternId = inst.exp.patterns[0];
113
- const inputPatternNodeId = typePatternGraph.find(inputPatternId);
114
- const inputPropertyList = patternToPropertyList(exportPatternGraph(typePatternGraph, inputPatternNodeId));
115
- return inputPropertyList[0][1];
116
- }
117
-
118
111
  const inputPropertyList = getPattern(inst.src, inputMap);
119
- const inputRoot = inputPropertyList?.[0];
120
- if (!Array.isArray(inputRoot) || !Array.isArray(inputRoot[1])) {
121
- throw new Error(`Wasm compiler: cannot infer input pattern for field '${inst.label}'`);
112
+ if (Array.isArray(inputPropertyList) && inputPropertyList.length > 0) {
113
+ const inputRoot = inputPropertyList[0];
114
+ if ((inputRoot[0] === "open-product" || inputRoot[0] === "closed-product") && Array.isArray(inputRoot[1])) {
115
+ return inputRoot[1];
116
+ }
122
117
  }
123
- if (inputRoot[0] !== "open-product" && inputRoot[0] !== "closed-product") {
124
- throw new Error(`Wasm compiler: field '${inst.label}' requires a product input pattern`);
118
+
119
+ if (typePatternGraph && inst.exp?.patterns?.[0] != null) {
120
+ try {
121
+ const inputPatternId = inst.exp.patterns[0];
122
+ const inputPatternNodeId = typePatternGraph.find(inputPatternId);
123
+ const inputPropertyList = patternToPropertyList(exportPatternGraph(typePatternGraph, inputPatternNodeId));
124
+ if (Array.isArray(inputPropertyList?.[0]?.[1])) {
125
+ return inputPropertyList[0][1];
126
+ }
127
+ } catch {
128
+ // Ignore fallback errors
129
+ }
125
130
  }
126
- return inputRoot[1];
131
+
132
+ return null;
127
133
  };
128
134
 
129
135
  const registers = new Set();
@@ -205,14 +211,12 @@ export function lowerToWasm(relDef, name, options = {}) {
205
211
  const lines = [];
206
212
  for (const field of inputProductFields) {
207
213
  const fieldIndex = inputProductEdges.findIndex(([label]) => label === field.label);
214
+ const tagId = getTagId(field.label);
208
215
  lines.push(` ;; cache input field ${field.label} in $${field.local}`);
209
216
  lines.push(` local.get $in`);
210
- lines.push(` local.get $in`);
211
- lines.push(` i32.const ${8 + 4 * fieldIndex}`);
212
- lines.push(` i32.add`);
213
- lines.push(` i32.load`);
214
- lines.push(` i32.add`);
215
- lines.push(` i32.load`);
217
+ lines.push(` i32.const ${tagId}`);
218
+ lines.push(` i32.const ${fieldIndex}`);
219
+ lines.push(` call $product_get`);
216
220
  lines.push(` local.set $${field.local}`);
217
221
  }
218
222
  return lines.join("\n");
@@ -231,9 +235,10 @@ export function lowerToWasm(relDef, name, options = {}) {
231
235
  lines.push(` i32.const ${inputProductFields.length}`);
232
236
  lines.push(` i32.store offset=4`);
233
237
  for (const field of inputProductFields) {
238
+ const tagId = getTagId(field.label);
234
239
  const offsetVal = 8 + 4 * inputProductFields.length + 4 * field.index;
235
240
  lines.push(` local.get $${dest}`);
236
- lines.push(` i32.const ${offsetVal}`);
241
+ lines.push(` i32.const ${tagId}`);
237
242
  lines.push(` i32.store offset=${8 + 4 * field.index}`);
238
243
  lines.push(` local.get $${dest}`);
239
244
  lines.push(` local.get $${field.local}`);
@@ -314,14 +319,24 @@ export function lowerToWasm(relDef, name, options = {}) {
314
319
  break;
315
320
  }
316
321
 
317
- const edges = getProductEdges(inst, inputMap);
318
- const fieldIndex = edges.findIndex(([label]) => label === inst.label);
319
- if (fieldIndex === -1) {
320
- throw new Error(`Wasm compiler: field '${inst.label}' not found in input pattern`);
322
+ let predictedIndex = -1;
323
+ try {
324
+ const edges = getProductEdges(inst, inputMap);
325
+ if (Array.isArray(edges)) {
326
+ predictedIndex = edges.findIndex(([label]) => label === inst.label);
327
+ }
328
+ } catch {
329
+ predictedIndex = -1;
321
330
  }
331
+ const tagId = getTagId(inst.label);
322
332
 
323
- lines.push(` ;; project_field ${inst.label} (index ${fieldIndex}) from $${src} to $${dest}`);
333
+ lines.push(` ;; project_field ${inst.label} (tagId ${tagId}, hint ${predictedIndex}) from $${src} to $${dest}`);
324
334
  lines.push(` local.get $${src}`);
335
+ lines.push(` i32.const ${tagId}`);
336
+ lines.push(` i32.const ${predictedIndex}`);
337
+ lines.push(` call $product_get`);
338
+ lines.push(` local.set $${dest}`);
339
+ lines.push(` local.get $${dest}`);
325
340
  lines.push(` i32.eqz`);
326
341
  lines.push(` if`);
327
342
  if (failTarget) {
@@ -332,14 +347,6 @@ export function lowerToWasm(relDef, name, options = {}) {
332
347
  lines.push(` return`);
333
348
  }
334
349
  lines.push(` end`);
335
- lines.push(` local.get $${src}`);
336
- lines.push(` local.get $${src}`);
337
- lines.push(` i32.const ${8 + 4 * fieldIndex}`);
338
- lines.push(` i32.add`);
339
- lines.push(` i32.load`);
340
- lines.push(` i32.add`);
341
- lines.push(` i32.load`);
342
- lines.push(` local.set $${dest}`);
343
350
  setPattern(dest, inst.pattern);
344
351
  break;
345
352
  }
@@ -443,11 +450,13 @@ export function lowerToWasm(relDef, name, options = {}) {
443
450
  lines.push(` i32.store offset=4`);
444
451
 
445
452
  for (let i = 0; i < N; i++) {
453
+ const branch = sortedBranches[i];
454
+ const tagId = getTagId(branch.label);
446
455
  const offsetVal = 8 + 4 * N + 4 * i;
447
456
  const fieldTmp = `${dest}_f${i}`;
448
457
 
449
458
  lines.push(` local.get $${dest}`);
450
- lines.push(` i32.const ${offsetVal}`);
459
+ lines.push(` i32.const ${tagId}`);
451
460
  lines.push(` i32.store offset=${8 + 4 * i}`);
452
461
 
453
462
  lines.push(` local.get $${dest}`);
@@ -501,6 +510,20 @@ export function lowerToWasm(relDef, name, options = {}) {
501
510
  }
502
511
  lines.push(` end`);
503
512
 
513
+ lines.push(` local.get $${src}`);
514
+ lines.push(` i32.load offset=0`);
515
+ lines.push(` i32.const 12`);
516
+ lines.push(` i32.ne`);
517
+ lines.push(` if`);
518
+ if (failTarget) {
519
+ lines.push(` br ${failTarget}`);
520
+ } else {
521
+ lines.push(` i32.const 0`);
522
+ lines.push(` i32.const 0`);
523
+ lines.push(` return`);
524
+ }
525
+ lines.push(` end`);
526
+
504
527
  lines.push(` local.get $${src}`);
505
528
  lines.push(` i32.load offset=4`);
506
529
  lines.push(` i32.const ${tagId}`);
@@ -13,7 +13,8 @@ import {
13
13
  patternToPropertyList,
14
14
  propertyListToPattern,
15
15
  Value,
16
- specializeKVM
16
+ specializeKVM,
17
+ specializeObjectRelations
17
18
  } from "@fraczak/k/backend-api.mjs";
18
19
  import { intersectPropertyListPatterns } from "@fraczak/k/codecs/runtime/codec.mjs";
19
20
  import { propertyListToFilter } from "@fraczak/k/codecs/runtime/show-value.mjs";
@@ -304,7 +305,8 @@ function compileModule(mainRelName, defs) {
304
305
 
305
306
  kvmFunc.name = nameMap.get(name) || cleanName(name);
306
307
  cleanCallNames(kvmFunc.body, nameMap);
307
- wats.push(lowerToWasm(kvmFunc, kvmFunc.name));
308
+ const wasmText = lowerToWasm(kvmFunc, kvmFunc.name);
309
+ wats.push(wasmText);
308
310
  }
309
311
 
310
312
  return {
@@ -437,7 +439,8 @@ function relTypeStatus(defs, name) {
437
439
  const rel = defs.rels?.[name];
438
440
  const explicit = rel?.typeDerivation?.status;
439
441
  if (explicit) return explicit;
440
- const scc = compileStatsByRelName(defs.compileStats).get(name);
442
+ const stats = compileStatsByRelName(defs.compileStats);
443
+ const scc = stats.get(name) || (name.includes("$") ? stats.get(name.split("$")[0]) : null);
441
444
  if (scc) return scc.converged ? "converged" : "not-converged";
442
445
  return "unknown";
443
446
  }
@@ -611,8 +614,10 @@ async function compileWasmArtifactFromObject(object, { entry = null, inputEnvelo
611
614
  if (!mainRel) throw new Error(`No main relation (${mainRelName}) defined in object`);
612
615
  const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
613
616
  const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
614
- if (classifyTyping(inputPattern, outputPattern).program === "polymorphic") {
615
- defs = annotateObjectWithInputFilter(object, mainRelName, inputPattern, inputEnvelopePattern);
617
+ const typing = classifyTyping(inputPattern, outputPattern);
618
+ if (typing.program === "polymorphic") {
619
+ const specialized = specializeObjectRelations(object, mainRelName, inputEnvelopePattern);
620
+ defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
616
621
  typingMode = "specialized";
617
622
  }
618
623
  }
@@ -635,7 +640,9 @@ async function compileWasmArtifact(
635
640
  const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
636
641
  const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
637
642
  if (classifyTyping(inputPattern, outputPattern).program === "polymorphic") {
638
- defs = annotateSourceWithInputFilter(fullSource, inputPattern, inputEnvelopePattern, options);
643
+ const obj = { format: "k-object", rels: defs.rels, relAlias: defs.relAlias, compileStats: defs.compileStats, main: "__main__" };
644
+ const specialized = specializeObjectRelations(obj, "__main__", inputEnvelopePattern);
645
+ defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
639
646
  typingMode = "specialized";
640
647
  }
641
648
  }
@@ -723,20 +730,18 @@ function readArenaValue(exports, ptr, pattern, patternNodeId, patternPropertyLis
723
730
 
724
731
  if (patternNode.kind === NODE_KIND.OPEN_PRODUCT || patternNode.kind === NODE_KIND.CLOSED_PRODUCT) {
725
732
  const N = view.getUint32(frame.ptr + 4, true);
726
- if (N !== patternNode.edges.length) {
727
- throw new Error(`Cannot decode product pointer ${frame.ptr}: arena field count ${N} does not match output pattern`);
728
- }
729
733
  const product = {};
730
734
  frame.assign(Value.product(product, patternForNode(patternPropertyList, frame.patternNodeId)));
731
735
  for (let i = N - 1; i >= 0; i--) {
732
- const edge = patternNode.edges[i];
733
- const offset = view.getUint32(frame.ptr + 8 + 4 * i, true);
734
- const childPtr = view.getUint32(frame.ptr + offset, true);
736
+ const tagId = view.getUint32(frame.ptr + 8 + 4 * i, true);
737
+ const label = tags.getTag(tagId) || patternNode.edges[i]?.label;
738
+ const edge = patternNode.edges.find((candidate) => candidate.label === label);
739
+ const childPtr = view.getUint32(frame.ptr + 8 + 4 * N + 4 * i, true);
735
740
  stack.push({
736
741
  ptr: wasmPtr(childPtr),
737
- patternNodeId: edge.target,
742
+ patternNodeId: edge?.target ?? null,
738
743
  assign(value) {
739
- product[edge.label] = value;
744
+ product[label] = value;
740
745
  }
741
746
  });
742
747
  }
@@ -787,8 +792,9 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
787
792
  view.setUint32(frame.ptr, 8 + 8 * N, true);
788
793
  view.setUint32(frame.ptr + 4, N, true);
789
794
  for (let i = 0; i < N; i++) {
795
+ const tagId = tags.getId(frame.fields[i].label);
790
796
  const offset = 8 + 4 * N + 4 * i;
791
- view.setUint32(frame.ptr + 8 + 4 * i, offset, true);
797
+ view.setUint32(frame.ptr + 8 + 4 * i, tagId, true);
792
798
  view.setUint32(frame.ptr + offset, frame.childPtrs[i], true);
793
799
  }
794
800
  arenaValues.set(frame.ptr, valueWithPatternNode(frame.value, patternPropertyList, frame.patternNodeId));
@@ -820,6 +826,13 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
820
826
  ? Object.keys(frame.value.product)
821
827
  .sort()
822
828
  .map((label) => ({ label, patternNodeId: frame.patternNodeId }))
829
+ : isOpenProduct
830
+ ? Object.keys(frame.value.product)
831
+ .sort()
832
+ .map((label) => {
833
+ const edge = patternNode.edges.find((e) => e.label === label);
834
+ return { label, patternNodeId: edge ? edge.target : null };
835
+ })
823
836
  : patternNode.edges.map((edge) => ({ label: edge.label, patternNodeId: edge.target }));
824
837
  const fieldLabels = new Set(fields.map(({ label }) => label));
825
838
  if (isClosedProduct) {
@@ -829,9 +842,17 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
829
842
  }
830
843
  }
831
844
  }
832
- for (const { label } of fields) {
833
- if (!Object.hasOwn(frame.value.product, label)) {
834
- throw new Error(`Product field '${label}' is required by input pattern node ${frame.patternNodeId}`);
845
+ if (isOpenProduct) {
846
+ for (const edge of patternNode.edges) {
847
+ if (!Object.hasOwn(frame.value.product, edge.label)) {
848
+ throw new Error(`Product field '${edge.label}' is required by input pattern node ${frame.patternNodeId}`);
849
+ }
850
+ }
851
+ } else {
852
+ for (const { label } of fields) {
853
+ if (!Object.hasOwn(frame.value.product, label)) {
854
+ throw new Error(`Product field '${label}' is required by input pattern node ${frame.patternNodeId}`);
855
+ }
835
856
  }
836
857
  }
837
858
 
@@ -841,6 +862,7 @@ function writeValueToArena(exports, value, pattern, patternNodeId, arenaValues,
841
862
  finishProduct: true,
842
863
  value: frame.value,
843
864
  patternNodeId: frame.patternNodeId,
865
+ fields,
844
866
  ptr,
845
867
  childPtrs,
846
868
  assign: frame.assign
@@ -200,8 +200,7 @@ export function readArenaValue(exports, rootPtr, pattern, rootPatternNodeId, pat
200
200
 
201
201
  for (let i = N - 1; i >= 0; i--) {
202
202
  const edge = patternNode.edges[i];
203
- const offsetVal = view.getUint32(ptr + 8 + 4 * i, true);
204
- const childPtr = view.getUint32(ptr + offsetVal, true);
203
+ const childPtr = view.getUint32(ptr + 8 + 4 * N + 4 * i, true);
205
204
  const label = edge.label;
206
205
  stack.push({
207
206
  ptr: childPtr,
@@ -253,14 +252,14 @@ export function writeValueToArena(exports, rootValue, pattern, rootPatternNodeId
253
252
  const frame = stack.pop();
254
253
 
255
254
  if (frame.finishProduct) {
256
- const { ptr, totalSize, N, childPtrs, assign } = frame;
255
+ const { ptr, totalSize, N, keys, childPtrs, assign } = frame;
257
256
  const view = new DataView(exports.memory.buffer);
258
257
  view.setUint32(ptr, totalSize, true);
259
258
  view.setUint32(ptr + 4, N, true);
260
259
 
261
260
  for (let i = 0; i < N; i++) {
262
261
  const offsetVal = 8 + 4 * N + 4 * i;
263
- view.setUint32(ptr + 8 + 4 * i, offsetVal, true);
262
+ view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
264
263
  view.setUint32(ptr + offsetVal, childPtrs[i], true);
265
264
  }
266
265
  assign(ptr);
@@ -294,6 +293,7 @@ export function writeValueToArena(exports, rootValue, pattern, rootPatternNodeId
294
293
  ptr,
295
294
  totalSize,
296
295
  N,
296
+ keys,
297
297
  childPtrs,
298
298
  assign
299
299
  });
@@ -194,7 +194,7 @@ console.log("==> Running Product Operations Integration Tests");
194
194
  for (let i = 0; i < N; i++) {
195
195
  const offsetVal = 8 + 4 * N + 4 * i;
196
196
  const val = fieldsMap[keys[i]];
197
- view.setUint32(ptr + 8 + 4 * i, offsetVal, true);
197
+ view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
198
198
  view.setUint32(ptr + offsetVal, val, true);
199
199
  }
200
200
  return ptr;
@@ -215,8 +215,8 @@ console.log("==> Running Product Operations Integration Tests");
215
215
  const view = new DataView(exports.memory.buffer);
216
216
  assert.equal(view.getUint32(ptrOut, true), 24, "Product total size should be 24");
217
217
  assert.equal(view.getUint32(ptrOut + 4, true), 2, "Product field count should be 2");
218
- assert.equal(view.getUint32(ptrOut + 8, true), 16, "Offset of first field (x) should be 16");
219
- assert.equal(view.getUint32(ptrOut + 12, true), 20, "Offset of second field (y) should be 20");
218
+ assert.equal(view.getUint32(ptrOut + 8, true), getTagId("x"), "TagId of first field (x)");
219
+ assert.equal(view.getUint32(ptrOut + 12, true), getTagId("y"), "TagId of second field (y)");
220
220
 
221
221
  // The first field value should be the projection of x from ptrIn (101)
222
222
  const valXPtr = view.getUint32(ptrOut + 16, true);
@@ -443,12 +443,12 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
443
443
  const runtimeWat = fs.readFileSync("runtime.wat", "utf8");
444
444
 
445
445
  const script = `
446
- $ nat = < {} 0, nat +1 >;
447
- 0 = {} | 0 $ nat;
448
- inc = | +1 $ nat;
449
- dec = $ nat / +1;
450
- add = $ { nat x, nat y } <
451
- { . x dec x, . y inc y } add,
446
+ nat? = ?< {} 0, nat? +1 >;
447
+ 0 = {} | 0 nat?;
448
+ inc = | +1 nat?;
449
+ dec = nat? / +1;
450
+ add = ?{ nat? x, nat? y } = X <
451
+ { . x dec x, . y inc y } ?X add,
452
452
  . y
453
453
  >;
454
454
  ()
@@ -483,8 +483,7 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
483
483
 
484
484
  for (let i = 0; i < N; i++) {
485
485
  const edge = patternNode.edges[i];
486
- const offsetVal = view.getUint32(ptr + 8 + 4 * i, true);
487
- const childPtr = view.getUint32(ptr + offsetVal, true);
486
+ const childPtr = view.getUint32(ptr + 8 + 4 * N + 4 * i, true);
488
487
  productObj[edge.label] = readArenaValue(childPtr, pattern, edge.target);
489
488
  }
490
489
  return Value.product(productObj, pattern);
@@ -530,7 +529,7 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
530
529
 
531
530
  for (let i = 0; i < N; i++) {
532
531
  const offsetVal = 8 + 4 * N + 4 * i;
533
- view.setUint32(ptr + 8 + 4 * i, offsetVal, true);
532
+ view.setUint32(ptr + 8 + 4 * i, getTagId(keys[i]), true);
534
533
  view.setUint32(ptr + offsetVal, childPtrs[i], true);
535
534
  }
536
535
  return ptr;
@@ -580,8 +579,8 @@ console.log("==> Running End-to-End Peano Addition & Serialization Tests");
580
579
  const view = new DataView(exports.memory.buffer);
581
580
  view.setUint32(ptr, 24, true);
582
581
  view.setUint32(ptr + 4, 2, true);
583
- view.setUint32(ptr + 8, 16, true);
584
- view.setUint32(ptr + 12, 20, true);
582
+ view.setUint32(ptr + 8, getTagId("x"), true);
583
+ view.setUint32(ptr + 12, getTagId("y"), true);
585
584
  view.setUint32(ptr + 16, xPtr, true);
586
585
  view.setUint32(ptr + 20, yPtr, true);
587
586
  return ptr;