aontu 0.55.0 → 0.57.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/README.md +3 -3
- package/dist/agentsmd.d.ts +1 -0
- package/dist/agentsmd.js +10 -3
- package/dist/agentsmd.js.map +1 -1
- package/dist/aontu.d.ts +4 -2
- package/dist/aontu.js +5 -2
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +10 -3
- package/dist/cli.js +311 -53
- package/dist/cli.js.map +1 -1
- package/dist/diff.d.ts +1 -0
- package/dist/diff.js +3 -2
- package/dist/diff.js.map +1 -1
- package/dist/escape.d.ts +5 -0
- package/dist/escape.js +455 -0
- package/dist/escape.js.map +1 -0
- package/dist/format.d.ts +26 -0
- package/dist/format.js +1489 -0
- package/dist/format.js.map +1 -0
- package/dist/hints.js +39 -1
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.d.ts +1 -0
- package/dist/jsonschema.js +3 -2
- package/dist/jsonschema.js.map +1 -1
- package/dist/lang.js +84 -6
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +2 -1
- package/dist/lsp.js +4 -4
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +2 -2
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +6 -4
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +8 -7
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +6 -6
- package/dist/mod.js.map +1 -1
- package/dist/patch.d.ts +1 -0
- package/dist/patch.js +1 -0
- package/dist/patch.js.map +1 -1
- package/dist/query.d.ts +1 -0
- package/dist/query.js +4 -3
- package/dist/query.js.map +1 -1
- package/dist/reach.d.ts +1 -0
- package/dist/reach.js +3 -2
- package/dist/reach.js.map +1 -1
- package/dist/relation.d.ts +1 -0
- package/dist/relation.js +3 -2
- package/dist/relation.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/std.js +2 -1
- package/dist/std.js.map +1 -1
- package/dist/subsume.d.ts +1 -0
- package/dist/subsume.js +3 -2
- package/dist/subsume.js.map +1 -1
- package/dist/trim.d.ts +1 -0
- package/dist/trim.js +3 -2
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +1 -0
- package/dist/type.js.map +1 -1
- package/dist/utility.d.ts +8 -2
- package/dist/utility.js +9 -1
- package/dist/utility.js.map +1 -1
- package/dist/val/EachFuncVal.js +1 -2
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +23 -0
- package/dist/val/EmitFuncVal.js +261 -0
- package/dist/val/EmitFuncVal.js.map +1 -0
- package/dist/val/FilterFuncVal.js +1 -2
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +16 -0
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +1 -2
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PlaceVal.d.ts +3 -1
- package/dist/val/PlaceVal.js +8 -6
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/StrFuncVal.d.ts +32 -0
- package/dist/val/StrFuncVal.js +292 -0
- package/dist/val/StrFuncVal.js.map +1 -0
- package/dist/vet.d.ts +1 -0
- package/dist/vet.js +1 -1
- package/dist/vet.js.map +1 -1
- package/dist/view.d.ts +2 -1
- package/dist/view.js +379 -8
- package/dist/view.js.map +1 -1
- package/grammar/aontu.abnf +156 -0
- package/grammar/aontu.gbnf +4 -3
- package/grammar/aontu.lark +4 -3
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +2 -1
- package/skill/grammar-card.md +5 -2
- package/src/agentsmd.ts +15 -3
- package/src/aontu.ts +8 -1
- package/src/cli.ts +365 -58
- package/src/diff.ts +7 -2
- package/src/escape.ts +371 -0
- package/src/format.ts +1728 -0
- package/src/hints.ts +53 -1
- package/src/jsonschema.ts +7 -1
- package/src/lang.ts +93 -6
- package/src/lsp.ts +8 -6
- package/src/mcp-server.ts +3 -2
- package/src/mcp.ts +6 -4
- package/src/mod-tool.ts +9 -8
- package/src/mod.ts +6 -6
- package/src/patch.ts +7 -0
- package/src/query.ts +8 -4
- package/src/reach.ts +7 -2
- package/src/relation.ts +7 -2
- package/src/sigdecl.ts +1 -1
- package/src/std.ts +2 -1
- package/src/subsume.ts +7 -2
- package/src/trim.ts +7 -2
- package/src/type.ts +8 -0
- package/src/utility.ts +27 -2
- package/src/val/EachFuncVal.ts +1 -3
- package/src/val/EmitFuncVal.ts +401 -0
- package/src/val/FilterFuncVal.ts +1 -3
- package/src/val/FuncBaseVal.ts +18 -0
- package/src/val/PackFuncVal.ts +1 -3
- package/src/val/PlaceVal.ts +8 -6
- package/src/val/StrFuncVal.ts +334 -0
- package/src/vet.ts +9 -3
- package/src/view.ts +442 -9
package/dist/view.js
CHANGED
|
@@ -7,8 +7,11 @@ exports.viewTree = viewTree;
|
|
|
7
7
|
exports.viewSet = viewSet;
|
|
8
8
|
// THE VIEWS (docs/design/VIEWS.0.md and VIEWS-ORDER.0.md): figures of
|
|
9
9
|
// an evaluated document, drawn as deterministic text a golden diff can
|
|
10
|
-
// check.
|
|
10
|
+
// check. Nine kinds:
|
|
11
11
|
//
|
|
12
|
+
// doc the shape of the document itself
|
|
13
|
+
// lattice the language's value lattice, with the document's own
|
|
14
|
+
// values placed on it
|
|
12
15
|
// tree the dependency tree of one relation
|
|
13
16
|
// matrix the dependency matrix over one relation, in canon or
|
|
14
17
|
// partition order, with closure and the unmirrored mark
|
|
@@ -50,6 +53,7 @@ const keyorder_1 = require("./keyorder");
|
|
|
50
53
|
const provenance_1 = require("./provenance");
|
|
51
54
|
const query_1 = require("./query");
|
|
52
55
|
const subsume_1 = require("./subsume");
|
|
56
|
+
const utility_1 = require("./utility");
|
|
53
57
|
// The text profile's mechanism: the eight named colours, `bold` and
|
|
54
58
|
// `dim`, and nothing else. `label` is unstyled -- an entity's own name
|
|
55
59
|
// is the figure's content, not a mark about it.
|
|
@@ -71,6 +75,7 @@ const styleOf = (style, as) => style ?? ('svg' === as ? 'css' : 'none');
|
|
|
71
75
|
// node-link drawing and no sensible Mermaid form of a matrix.
|
|
72
76
|
const PROFILES = {
|
|
73
77
|
doc: ['text', 'svg'],
|
|
78
|
+
lattice: ['text', 'svg'],
|
|
74
79
|
tree: ['text', 'svg'],
|
|
75
80
|
matrix: ['text', 'svg'],
|
|
76
81
|
graph: ['mermaid', 'dot', 'er'],
|
|
@@ -593,6 +598,368 @@ function treeSvg(rows, about, style) {
|
|
|
593
598
|
// drawn and the row says how many keys were not drawn, because a tree
|
|
594
599
|
// that stops without saying so is the one thing a structural drawing
|
|
595
600
|
// must not be.
|
|
601
|
+
// ---------------------------------------------------------------------
|
|
602
|
+
// THE VALUE LATTICE, and where this document's values sit on it.
|
|
603
|
+
//
|
|
604
|
+
// THE SCAFFOLD IS THE LANGUAGE'S, NOT THE DOCUMENT'S: `top` at the
|
|
605
|
+
// join, the four kind families under it, `path()` under `string`, the
|
|
606
|
+
// four numeric leaves under `number`, and `nil` at the meet. Every
|
|
607
|
+
// Aontu document is drawn against the SAME shape, which is what makes
|
|
608
|
+
// two of these figures comparable -- and what makes this a view of the
|
|
609
|
+
// language that a document annotates, rather than a picture assembled
|
|
610
|
+
// out of whatever the document happened to contain.
|
|
611
|
+
//
|
|
612
|
+
// See docs/unification.md for what the ordering means.
|
|
613
|
+
// The scaffold: each kind and the one above it. The ENGINE decides
|
|
614
|
+
// which kind sits under which -- kindParent in ts/src/val/ScalarKindVal.ts,
|
|
615
|
+
// and its twin in go/scalar.go -- and a test in each port holds this
|
|
616
|
+
// table to it, so adding a kind to the engine makes the figure grow a
|
|
617
|
+
// node rather than quietly leave one out.
|
|
618
|
+
const LATTICE_PARENT = [
|
|
619
|
+
['string', 'top'],
|
|
620
|
+
['path()', 'string'],
|
|
621
|
+
['number', 'top'],
|
|
622
|
+
['integer', 'number'],
|
|
623
|
+
['float', 'number'],
|
|
624
|
+
['biginteger', 'number'],
|
|
625
|
+
['bigdecimal', 'number'],
|
|
626
|
+
['boolean', 'top'],
|
|
627
|
+
['null', 'top'],
|
|
628
|
+
];
|
|
629
|
+
// The columns, left to right: the MINIMAL kinds, the ones with nothing
|
|
630
|
+
// under them. Everything else is drawn centred over the columns it
|
|
631
|
+
// covers, so this list alone fixes the figure's horizontal order -- and
|
|
632
|
+
// it puts the kinds that reach the bottom from higher up (`boolean`,
|
|
633
|
+
// `null`) on the outside, where their lines pass the numeric fan
|
|
634
|
+
// rather than crossing it.
|
|
635
|
+
const LATTICE_COLS = ['path()', 'integer', 'float', 'biginteger', 'bigdecimal', 'boolean',
|
|
636
|
+
'null'];
|
|
637
|
+
// The rows, top to bottom. `top` and `nil` are the endpoints and are
|
|
638
|
+
// not kinds: no `superior()` answers either, and no entry above names
|
|
639
|
+
// them as a parent.
|
|
640
|
+
const LATTICE_ROWS = [
|
|
641
|
+
['top'],
|
|
642
|
+
['string', 'number', 'boolean', 'null'],
|
|
643
|
+
['path()', 'integer', 'float', 'biginteger', 'bigdecimal'],
|
|
644
|
+
['nil'],
|
|
645
|
+
];
|
|
646
|
+
const LATTICE_NODES = ['top', ...LATTICE_PARENT.map(([name]) => name), 'nil'];
|
|
647
|
+
// Every node at or above one, itself included.
|
|
648
|
+
function latticeAncestors(name) {
|
|
649
|
+
const out = [name];
|
|
650
|
+
for (let at = name; '' !== at;) {
|
|
651
|
+
const row = LATTICE_PARENT.find(([child]) => child === at);
|
|
652
|
+
at = undefined === row ? '' : row[1];
|
|
653
|
+
if ('' !== at) {
|
|
654
|
+
out.push(at);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return out;
|
|
658
|
+
}
|
|
659
|
+
// The columns one node covers: its own if it is minimal, otherwise
|
|
660
|
+
// every column beneath it. `nil` is beneath everything and above
|
|
661
|
+
// nothing, so the walk finds no column under it and the whole width is
|
|
662
|
+
// its span -- which is where it belongs.
|
|
663
|
+
function latticeSpan(name) {
|
|
664
|
+
const own = LATTICE_COLS.indexOf(name);
|
|
665
|
+
if (-1 !== own) {
|
|
666
|
+
return [own];
|
|
667
|
+
}
|
|
668
|
+
const under = LATTICE_COLS
|
|
669
|
+
.map((col, i) => latticeAncestors(col).includes(name) ? i : -1)
|
|
670
|
+
.filter((i) => -1 !== i);
|
|
671
|
+
return 0 === under.length ? LATTICE_COLS.map((_, i) => i) : under;
|
|
672
|
+
}
|
|
673
|
+
// True when `parent` is immediately above `child`. NIL IS COVERED BY
|
|
674
|
+
// EVERY MINIMAL KIND: it is the meet of all of them, and the only node
|
|
675
|
+
// the parent table does not name, because nothing in the engine ever
|
|
676
|
+
// answers `nil` as a superior.
|
|
677
|
+
function latticeCovers(parent, child) {
|
|
678
|
+
return 'nil' === child
|
|
679
|
+
? -1 !== LATTICE_COLS.indexOf(parent)
|
|
680
|
+
: LATTICE_PARENT.some(([c, p]) => c === child && p === parent);
|
|
681
|
+
}
|
|
682
|
+
// WHERE ONE VALUE SITS, or undefined for a value that is not at a
|
|
683
|
+
// single point. The answers are the kinds of thing a document holds:
|
|
684
|
+
//
|
|
685
|
+
// a CONCRETE scalar sits at its kind -- `8080` is an `integer`, and
|
|
686
|
+
// `superior()` is the lattice's own answer to which;
|
|
687
|
+
// a KIND MARKER sits AT that kind -- `integer` written as a schema
|
|
688
|
+
// is the node itself, not a value under it;
|
|
689
|
+
// everything else -- a constraint, an unresolved disjunction, a
|
|
690
|
+
// reference -- is not one point. `integer & min(1)` is a REGION of
|
|
691
|
+
// the lattice and `*8080 | integer` is two places at once, so
|
|
692
|
+
// drawing either at a node would be a claim the figure cannot
|
|
693
|
+
// support. Both are counted into the loss report instead.
|
|
694
|
+
function latticePoint(v) {
|
|
695
|
+
const node = throughDoc(v);
|
|
696
|
+
if (true === node?.isNil) {
|
|
697
|
+
return 'nil';
|
|
698
|
+
}
|
|
699
|
+
if (true === node?.isTop) {
|
|
700
|
+
return 'top';
|
|
701
|
+
}
|
|
702
|
+
// A kind marker names its own node; a concrete scalar names the node
|
|
703
|
+
// above it. Either way the name has to BE one of the figure's: a
|
|
704
|
+
// kind the scaffold does not draw has nowhere to go, and saying so
|
|
705
|
+
// through the loss report is the only honest answer.
|
|
706
|
+
const name = true === node?.isScalarKind ? String(node.canon)
|
|
707
|
+
: true === node?.isScalar ? String(node.superior?.().canon) : '';
|
|
708
|
+
return LATTICE_NODES.includes(name) ? name : undefined;
|
|
709
|
+
}
|
|
710
|
+
// The document's own values, gathered by lattice node. Containers are
|
|
711
|
+
// walked but not placed: a map is not a scalar lattice citizen, and
|
|
712
|
+
// counting one at `top` would put every document's root there.
|
|
713
|
+
function latticeCensus(root, at) {
|
|
714
|
+
const counts = new Map();
|
|
715
|
+
const unplaced = [];
|
|
716
|
+
const stack = [{ node: root, path: at }];
|
|
717
|
+
while (0 < stack.length) {
|
|
718
|
+
const { node, path } = stack.pop();
|
|
719
|
+
const kids = docKids(node);
|
|
720
|
+
if (0 < kids.length) {
|
|
721
|
+
// A container is a shape, not a point: walk into it and place
|
|
722
|
+
// what it holds.
|
|
723
|
+
for (const key of kids) {
|
|
724
|
+
stack.push({
|
|
725
|
+
node: throughDoc(throughDoc(node).peg[key]),
|
|
726
|
+
path: path + '.' + key,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
const point = latticePoint(node);
|
|
732
|
+
if (undefined === point) {
|
|
733
|
+
// AN EMPTY CONTAINER IS NEITHER A POINT NOR A SHAPE with
|
|
734
|
+
// anything in it, and is no more unplaced than `{}` is a value:
|
|
735
|
+
// skip it rather than report a loss a reader cannot act on.
|
|
736
|
+
const inner = throughDoc(node);
|
|
737
|
+
if (true !== inner?.isMap && true !== inner?.isList) {
|
|
738
|
+
unplaced.push(path);
|
|
739
|
+
}
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
const there = counts.get(point) ?? [];
|
|
743
|
+
there.push(path);
|
|
744
|
+
counts.set(point, there);
|
|
745
|
+
}
|
|
746
|
+
for (const paths of counts.values()) {
|
|
747
|
+
paths.sort(keyorder_1.cmpCodePoint);
|
|
748
|
+
}
|
|
749
|
+
unplaced.sort(keyorder_1.cmpCodePoint);
|
|
750
|
+
return { counts, unplaced };
|
|
751
|
+
}
|
|
752
|
+
// What one node is written as: its name, and the count of the
|
|
753
|
+
// document's values that landed on it. A node with nothing at it is
|
|
754
|
+
// still drawn -- the shape is the language's, and a figure that left
|
|
755
|
+
// the empty nodes out would be a different lattice for every document.
|
|
756
|
+
function latticeCell(counts, name) {
|
|
757
|
+
const n = (counts.get(name) ?? []).length;
|
|
758
|
+
return 0 === n ? name : `${name} (${n})`;
|
|
759
|
+
}
|
|
760
|
+
// The horizontal layout, in characters: one column per minimal kind,
|
|
761
|
+
// each as wide as the widest cell drawn over it plus a gutter, and the
|
|
762
|
+
// centre of each. The spanning nodes are narrower than the span they
|
|
763
|
+
// cover, so none of them needs a width of its own. The gutter is THREE
|
|
764
|
+
// because the SVG draws a box a character wider than its text: two of
|
|
765
|
+
// those characters are the box's own padding and the third is the gap
|
|
766
|
+
// between one box and the next.
|
|
767
|
+
const LATTICE_GUTTER = 3;
|
|
768
|
+
function latticeCols(counts) {
|
|
769
|
+
const w = LATTICE_COLS.map((col) => LATTICE_GUTTER + Math.max(...LATTICE_ROWS.flat()
|
|
770
|
+
.filter((name) => {
|
|
771
|
+
const span = latticeSpan(name);
|
|
772
|
+
return 1 === span.length && col === LATTICE_COLS[span[0]];
|
|
773
|
+
})
|
|
774
|
+
.map((name) => latticeCell(counts, name).length)));
|
|
775
|
+
let x = 0;
|
|
776
|
+
const cx = w.map((n) => {
|
|
777
|
+
const c = x + Math.floor(n / 2);
|
|
778
|
+
x += n;
|
|
779
|
+
return c;
|
|
780
|
+
});
|
|
781
|
+
return { cx, width: x };
|
|
782
|
+
}
|
|
783
|
+
// The centre of a node, from the columns it covers.
|
|
784
|
+
function latticeAt(name, cx) {
|
|
785
|
+
const span = latticeSpan(name);
|
|
786
|
+
return Math.round((cx[span[0]] + cx[span[span.length - 1]]) / 2);
|
|
787
|
+
}
|
|
788
|
+
// The box-drawing glyph for one column of a rule, from the four facts
|
|
789
|
+
// that meet there: whether the rule continues left and right, and
|
|
790
|
+
// whether a stem leaves upward and downward. Deciding it this way is
|
|
791
|
+
// what lets `number` -- which is BOTH one of the many under `top` and
|
|
792
|
+
// the one above the numeric leaves -- come out as the join it is,
|
|
793
|
+
// without a case written for it. The table is total, so no column has
|
|
794
|
+
// to be asked whether it has a glyph.
|
|
795
|
+
const LATTICE_GLYPH = {
|
|
796
|
+
'....': '─', '...d': '│', '..u.': '│', '..ud': '│',
|
|
797
|
+
'.r..': '─', '.r.d': '┌', '.ru.': '└', '.rud': '├',
|
|
798
|
+
'l...': '─', 'l..d': '┐', 'l.u.': '┘', 'l.ud': '┤',
|
|
799
|
+
'lr..': '─', 'lr.d': '┬', 'lru.': '┴', 'lrud': '┼',
|
|
800
|
+
};
|
|
801
|
+
// The figure is PAINTED rather than assembled from padded strings: the
|
|
802
|
+
// nodes have to line up with the rules that join them, and a count
|
|
803
|
+
// changes a cell's width -- so the geometry is settled first, in
|
|
804
|
+
// columns, and every glyph is then written at a place already known.
|
|
805
|
+
function latticeText(counts, style) {
|
|
806
|
+
const paint = painter(style);
|
|
807
|
+
const { cx, width } = latticeCols(counts);
|
|
808
|
+
const canvas = [];
|
|
809
|
+
const roles = [];
|
|
810
|
+
const put = (y, x, text, role) => {
|
|
811
|
+
while (canvas.length <= y) {
|
|
812
|
+
canvas.push(new Array(width).fill(' '));
|
|
813
|
+
roles.push(new Array(width).fill('label'));
|
|
814
|
+
}
|
|
815
|
+
for (let i = 0; i < text.length; i++) {
|
|
816
|
+
canvas[y][x + i] = text[i];
|
|
817
|
+
roles[y][x + i] = role;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
// A cell is its name and, where the document reached it, the count:
|
|
821
|
+
// two roles, so a terminal can mute the second without touching the
|
|
822
|
+
// first.
|
|
823
|
+
const cell = (y, name) => {
|
|
824
|
+
const text = latticeCell(counts, name);
|
|
825
|
+
const left = latticeAt(name, cx) - Math.floor(text.length / 2);
|
|
826
|
+
put(y, left, name, 'label');
|
|
827
|
+
put(y, left + name.length, text.slice(name.length), 'muted');
|
|
828
|
+
};
|
|
829
|
+
const stems = (y, at) => {
|
|
830
|
+
for (const name of at) {
|
|
831
|
+
put(y, latticeAt(name, cx), '│', 'rule');
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
// The rule that joins one row to the next, plus the lines that pass
|
|
835
|
+
// it by: a kind with nothing under it runs on down the OUTSIDE of the
|
|
836
|
+
// fan, which the column order guarantees is clear of it.
|
|
837
|
+
const rule = (y, up, down, by) => {
|
|
838
|
+
const at = (names) => names.map((n) => latticeAt(n, cx));
|
|
839
|
+
const [u, d] = [at(up), at(down)];
|
|
840
|
+
const lo = Math.min(...u, ...d), hi = Math.max(...u, ...d);
|
|
841
|
+
for (let x = lo; x <= hi; x++) {
|
|
842
|
+
put(y, x, LATTICE_GLYPH[(x > lo ? 'l' : '.') + (x < hi ? 'r' : '.') +
|
|
843
|
+
(u.includes(x) ? 'u' : '.') + (d.includes(x) ? 'd' : '.')], 'rule');
|
|
844
|
+
}
|
|
845
|
+
stems(y, by);
|
|
846
|
+
};
|
|
847
|
+
// Four node rows and three joins. `open` is every node whose line
|
|
848
|
+
// downward has not been drawn yet, which is what carries `boolean`
|
|
849
|
+
// and `null` past the numeric row to the bottom rule.
|
|
850
|
+
let open = [];
|
|
851
|
+
let y = 0;
|
|
852
|
+
for (let r = 0; r < LATTICE_ROWS.length; r++) {
|
|
853
|
+
stems(y, open);
|
|
854
|
+
for (const name of LATTICE_ROWS[r]) {
|
|
855
|
+
cell(y, name);
|
|
856
|
+
}
|
|
857
|
+
open = [...open, ...LATTICE_ROWS[r]];
|
|
858
|
+
if (LATTICE_ROWS.length - 1 === r) {
|
|
859
|
+
break;
|
|
860
|
+
}
|
|
861
|
+
const next = LATTICE_ROWS[r + 1];
|
|
862
|
+
const parents = open.filter((n) => next.some((k) => latticeCovers(n, k)));
|
|
863
|
+
const by = open.filter((n) => !parents.includes(n));
|
|
864
|
+
stems(y + 1, open);
|
|
865
|
+
rule(y + 2, parents, next, by);
|
|
866
|
+
open = by;
|
|
867
|
+
y += 3;
|
|
868
|
+
}
|
|
869
|
+
return canvas.map((line, i) => {
|
|
870
|
+
const bare = line.join('').replace(/\s+$/, '');
|
|
871
|
+
let out = '', at = 0;
|
|
872
|
+
while (at < bare.length) {
|
|
873
|
+
let end = at;
|
|
874
|
+
while (end < bare.length && roles[i][end] === roles[i][at]) {
|
|
875
|
+
end++;
|
|
876
|
+
}
|
|
877
|
+
out += paint(roles[i][at], bare.slice(at, end));
|
|
878
|
+
at = end;
|
|
879
|
+
}
|
|
880
|
+
return out;
|
|
881
|
+
}).join('\n');
|
|
882
|
+
}
|
|
883
|
+
// The same figure as SVG, off the same column layout, so the two
|
|
884
|
+
// profiles are one drawing in two grammars rather than two drawings.
|
|
885
|
+
// A node the document REACHES is drawn with the ordinary rule stroke
|
|
886
|
+
// (`av-box`) and one it does not with the faint one (`av-cell`),
|
|
887
|
+
// because every node is drawn whether this document reaches it or not
|
|
888
|
+
// and a reader has to see which is which without counting. NO NEW
|
|
889
|
+
// CLASS: those two already mean a box and a faint box, so a host page
|
|
890
|
+
// that themed the other figures gets this one for nothing.
|
|
891
|
+
function latticeSvg(counts, at, style) {
|
|
892
|
+
const ROWH = 3 * LH;
|
|
893
|
+
const BOXH = 26;
|
|
894
|
+
const { cx, width } = latticeCols(counts);
|
|
895
|
+
const parts = [];
|
|
896
|
+
const rowOf = new Map();
|
|
897
|
+
LATTICE_ROWS.forEach((row, r) => row.forEach((name) => rowOf.set(name, r)));
|
|
898
|
+
const x = (name) => PAD + latticeAt(name, cx) * CH;
|
|
899
|
+
const y = (name) => PAD + BOXH / 2 + rowOf.get(name) * ROWH;
|
|
900
|
+
// Edges first, so a box always sits over the lines that reach it.
|
|
901
|
+
// The horizontal jog is placed just above the CHILD rather than
|
|
902
|
+
// halfway down, which is what keeps `boolean` and `null` -- three
|
|
903
|
+
// rows from `top` to `nil` with nothing between -- clear of the
|
|
904
|
+
// numeric row they pass.
|
|
905
|
+
const edges = [...LATTICE_PARENT,
|
|
906
|
+
...LATTICE_COLS.map((col) => ['nil', col])];
|
|
907
|
+
for (const [child, parent] of edges) {
|
|
908
|
+
const y2 = y(child) - BOXH / 2;
|
|
909
|
+
parts.push(svgPath(`M${x(parent)} ${y(parent) + BOXH / 2}` +
|
|
910
|
+
`V${y2 - (ROWH - BOXH) / 2}H${x(child)}V${y2}`, 'av-line'));
|
|
911
|
+
}
|
|
912
|
+
for (const name of LATTICE_ROWS.flat()) {
|
|
913
|
+
const text = latticeCell(counts, name);
|
|
914
|
+
const w = (text.length + 2) * CH;
|
|
915
|
+
parts.push(svgRect(x(name) - w / 2, y(name) - BOXH / 2, w, BOXH, name === text ? 'av-cell' : 'av-box'));
|
|
916
|
+
// The name and the count in ONE text element, as the tree does it:
|
|
917
|
+
// two runs on one baseline, so the count is muted without the
|
|
918
|
+
// figure having to place it.
|
|
919
|
+
parts.push(`<text x="${x(name)}" y="${y(name) + 5}" text-anchor="middle">` +
|
|
920
|
+
`<tspan class="av-t">${svgEsc(name)}</tspan>` +
|
|
921
|
+
`<tspan class="av-m">${svgEsc(text.slice(name.length))}</tspan></text>`);
|
|
922
|
+
}
|
|
923
|
+
const placed = [...counts.values()].reduce((n, p) => n + p.length, 0);
|
|
924
|
+
return svgDoc(width * CH + 2 * PAD, 2 * PAD + BOXH + (LATTICE_ROWS.length - 1) * ROWH, `Value lattice at ${at}: ${placed} value(s) placed`, parts, style);
|
|
925
|
+
}
|
|
926
|
+
// The figure. The row count is fixed -- the lattice is the language's,
|
|
927
|
+
// and no option makes it smaller -- so `--max-rows` below it is still a
|
|
928
|
+
// refusal, because a figure that quietly overran a stated bound is the
|
|
929
|
+
// thing every other kind here refuses to be; the message says raise
|
|
930
|
+
// rather than narrow.
|
|
931
|
+
const LATTICE_LINES = 3 * LATTICE_ROWS.length - 2;
|
|
932
|
+
function drawLattice(root, o, max, loss) {
|
|
933
|
+
const at = o.at ?? '$';
|
|
934
|
+
const anchor = (0, vet_1.anchorAt)(root, at);
|
|
935
|
+
if (null == anchor) {
|
|
936
|
+
// The same code and the same sentence `get` answers with, for the
|
|
937
|
+
// same question.
|
|
938
|
+
return {
|
|
939
|
+
errors: [finding('no_path', 'reference', at, `The path ${at} names nothing in this document.`)],
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
if (max < LATTICE_LINES) {
|
|
943
|
+
return {
|
|
944
|
+
errors: [finding('view_rows_exceeded', 'budget', '$', `The figure has ${LATTICE_LINES} rows, above --max-rows ${max}; ` +
|
|
945
|
+
'the value lattice is fixed, so raise the limit.', `rows: ${LATTICE_LINES}, max: ${max}`)],
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
const { counts, unplaced } = latticeCensus(anchor, at);
|
|
949
|
+
if (0 < unplaced.length) {
|
|
950
|
+
// NOT A LOSS OF DETAIL BUT A LOSS OF PLACE: these values are real,
|
|
951
|
+
// and the figure cannot say where they are because they are not
|
|
952
|
+
// anywhere single. Named, not merely counted -- a reader who sees
|
|
953
|
+
// `2` wants to know which two.
|
|
954
|
+
loss.push({
|
|
955
|
+
code: 'lattice_unplaced', count: unplaced.length, detail: unplaced,
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
return {
|
|
959
|
+
text: 'svg' === o.as
|
|
960
|
+
? latticeSvg(counts, at, o.style) : latticeText(counts, o.style),
|
|
961
|
+
};
|
|
962
|
+
}
|
|
596
963
|
const DEFAULT_DOC_DEPTH = 3;
|
|
597
964
|
// A node's own children, as the anchor walk sees them: map keys sorted
|
|
598
965
|
// by code point, list indices in order, and nothing for a leaf.
|
|
@@ -1593,7 +1960,7 @@ function drawLadder(src, options, as, max) {
|
|
|
1593
1960
|
errors: [finding('view_at_required', 'reference', '$', 'The ladder needs the path to draw; name it with --at.')],
|
|
1594
1961
|
};
|
|
1595
1962
|
}
|
|
1596
|
-
const rep = (0, query_1.why)(src, options.at, { path: options.path, trust: options.trust });
|
|
1963
|
+
const rep = (0, query_1.why)(src, options.at, { path: options.path, trust: options.trust, textExt: options.textExt });
|
|
1597
1964
|
if (undefined === rep.record) {
|
|
1598
1965
|
return { errors: rep.findings };
|
|
1599
1966
|
}
|
|
@@ -1640,7 +2007,7 @@ const compareBySubsume = (general, specific, options) => {
|
|
|
1640
2007
|
const r = (0, subsume_1.subsume)(general.src, specific.src, {
|
|
1641
2008
|
at: options.at, profile: options.profile,
|
|
1642
2009
|
generalPath: general.path, specificPath: specific.path,
|
|
1643
|
-
trust: options.trust,
|
|
2010
|
+
trust: options.trust, textExt: options.textExt,
|
|
1644
2011
|
});
|
|
1645
2012
|
return { verdict: r.verdict, code: r.findings[0]?.code ?? 'undecided' };
|
|
1646
2013
|
};
|
|
@@ -1796,7 +2163,7 @@ function drawPoset(docs, options, as, max, loss, compare) {
|
|
|
1796
2163
|
// on their own, each with its own finding, or the anchor a document
|
|
1797
2164
|
// lacks.
|
|
1798
2165
|
function docFailure(d, options) {
|
|
1799
|
-
const loaded = load(d.src, d.path, options
|
|
2166
|
+
const loaded = load(d.src, d.path, options, undefined);
|
|
1800
2167
|
if (undefined !== loaded.errors) {
|
|
1801
2168
|
return loaded.errors;
|
|
1802
2169
|
}
|
|
@@ -1808,8 +2175,8 @@ function docFailure(d, options) {
|
|
|
1808
2175
|
// One evaluation, parsed and unified separately so the provenance
|
|
1809
2176
|
// recorder can stamp the parsed tree before the fixpoint runs (`why`'s
|
|
1810
2177
|
// precedent).
|
|
1811
|
-
function load(src, path,
|
|
1812
|
-
const aontu = new aontu_1.Aontu(
|
|
2178
|
+
function load(src, path, include, prov) {
|
|
2179
|
+
const aontu = new aontu_1.Aontu((0, utility_1.includeOpts)(include));
|
|
1813
2180
|
const ctx = aontu.ctx({ collect: true, prov });
|
|
1814
2181
|
const parseOpts = null == path ? undefined : { path };
|
|
1815
2182
|
const parsed = aontu.parse(src, parseOpts, ctx);
|
|
@@ -1887,7 +2254,7 @@ function view(src, opts, hooks) {
|
|
|
1887
2254
|
}
|
|
1888
2255
|
const prov = 'layers' === kind
|
|
1889
2256
|
? (hooks?.provenance ?? (() => new provenance_1.Provenance()))() : undefined;
|
|
1890
|
-
const loaded = load(src, options.path, options
|
|
2257
|
+
const loaded = load(src, options.path, options, prov);
|
|
1891
2258
|
if (undefined !== loaded.errors) {
|
|
1892
2259
|
return done({ errors: loaded.errors });
|
|
1893
2260
|
}
|
|
@@ -1905,6 +2272,9 @@ function drawLoaded(root, ctx, gen, prov, kind, as, options, max, loss) {
|
|
|
1905
2272
|
if ('doc' === kind) {
|
|
1906
2273
|
return drawDoc(root, { ...options, as, style }, max, loss);
|
|
1907
2274
|
}
|
|
2275
|
+
if ('lattice' === kind) {
|
|
2276
|
+
return drawLattice(root, { ...options, as, style }, max, loss);
|
|
2277
|
+
}
|
|
1908
2278
|
if ('layers' === kind) {
|
|
1909
2279
|
return drawLayers(prov, root, options.path, { ...options, as, style }, max, loss);
|
|
1910
2280
|
}
|
|
@@ -2101,7 +2471,7 @@ function viewSet(src, opts, hooks) {
|
|
|
2101
2471
|
// always costs a little and makes the one-evaluation claim true for
|
|
2102
2472
|
// every kind but the ladder, which re-runs `why` by construction.
|
|
2103
2473
|
const prov = (hooks?.provenance ?? (() => new provenance_1.Provenance()))();
|
|
2104
|
-
const loaded = load(src, options.path, options
|
|
2474
|
+
const loaded = load(src, options.path, options, prov);
|
|
2105
2475
|
if (undefined !== loaded.errors) {
|
|
2106
2476
|
return { verdict: 'error', views: [], errors: loaded.errors };
|
|
2107
2477
|
}
|
|
@@ -2143,6 +2513,7 @@ function viewSet(src, opts, hooks) {
|
|
|
2143
2513
|
const loss = [];
|
|
2144
2514
|
const each = {
|
|
2145
2515
|
...plan.opts, path: options.path, trust: options.trust,
|
|
2516
|
+
textExt: options.textExt,
|
|
2146
2517
|
};
|
|
2147
2518
|
const fig = 'ladder' === plan.kind
|
|
2148
2519
|
? drawLadder(src, each, plan.as, plan.max)
|