@fraczak/k 7.2.0 → 8.1.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/CHANGELOG.md +90 -0
- package/DOCS/BIG_PICTURE.md +54 -7
- package/DOCS/CODECS.md +21 -13
- package/DOCS/CODEC_ARCHITECTURE.md +1 -1
- package/DOCS/DICTIONARY.md +13 -4
- package/DOCS/FILE_FORMATS.md +3 -2
- package/DOCS/JSON_APPROACHES_COMPARISON.md +302 -0
- package/DOCS/NAMED_PATTERNS.md +317 -0
- package/DOCS/PATTERNS.md +59 -11
- package/DOCS/REPL.md +13 -2
- package/DOCS/WEB_REPL_REQUIREMENTS.md +3 -2
- package/Examples/arithmetics.k +22 -17
- package/Examples/bnat.k +21 -11
- package/Examples/buda.k +37 -20
- package/Examples/byte.k +3 -1
- package/Examples/code.k +38 -39
- package/Examples/djson.k +789 -0
- package/Examples/esc.k +1 -1
- package/Examples/karatsuba-mult.k +16 -13
- package/Examples/list.k +8 -4
- package/Examples/nat.k +17 -11
- package/Examples/poly.k +11 -3
- package/Examples/vhdl_to_k.k +43 -31
- package/README.md +40 -16
- package/browser/repl-browser.mjs +2 -0
- package/browser/tree-view.mjs +35 -16
- package/browser/vfs-data.mjs +10 -8
- package/codecs/README.md +1 -1
- package/codecs/djson.mjs +375 -84
- package/core.k +79 -115
- package/index.mjs +3 -0
- package/named-patterns.mjs +267 -0
- package/object.mjs +31 -3
- package/package.json +1 -1
- package/parser.jison +68 -5
- package/parser.mjs +111 -60
- package/pretty.mjs +5 -0
- package/repl.mjs +188 -22
- package/scripts/build-repl-html.mjs +58 -4
- package/scripts/run-tests.mjs +10 -0
- package/symbol-table.mjs +20 -2
- package/tests/test-codecs.mjs +30 -17
- package/tests/test-djson-methods.mjs +285 -0
- package/tests/test-named-patterns.mjs +189 -0
- package/tests/test-repl-html.mjs +47 -4
- package/tests/test-repl.mjs +149 -0
package/Examples/esc.k
CHANGED
|
@@ -5,19 +5,21 @@
|
|
|
5
5
|
yes = {} | yes;
|
|
6
6
|
no = {} | no;
|
|
7
7
|
|
|
8
|
-
bits
|
|
8
|
+
bits[] = < {} _, B 0, B 1 > = B;
|
|
9
|
+
bits? = ? bits[];
|
|
9
10
|
0b = {} | _;
|
|
10
11
|
0 = 0b |0;
|
|
11
12
|
1 = 0b |1;
|
|
12
13
|
|
|
13
|
-
unat
|
|
14
|
+
unat[] = < {} _, U 1 > = U;
|
|
15
|
+
unat? = ? unat[];
|
|
14
16
|
0u = {} | _;
|
|
15
17
|
|
|
16
18
|
empty = < /_ yes, no >;
|
|
17
19
|
empty? = {empty /yes if, () then} .then;
|
|
18
20
|
|
|
19
21
|
inv = {()x, 0b r} _inv;
|
|
20
|
-
_inv = ?{
|
|
22
|
+
_inv = ?{ bits[] = X x, ... }
|
|
21
23
|
<
|
|
22
24
|
{.x empty? if, .r then} .then,
|
|
23
25
|
<
|
|
@@ -27,7 +29,7 @@ _inv = ?{ <X 0, X 1, {} _>=X x, ... }
|
|
|
27
29
|
>;
|
|
28
30
|
|
|
29
31
|
concat = {.x inv x, .y y} concat_;
|
|
30
|
-
concat_ = ?{
|
|
32
|
+
concat_ = ?{ bits[] = X x, ... }
|
|
31
33
|
<
|
|
32
34
|
{.x empty? if, .y then} .then,
|
|
33
35
|
<
|
|
@@ -50,7 +52,7 @@ c0 = {} | 0;
|
|
|
50
52
|
c1 = {} | 1;
|
|
51
53
|
|
|
52
54
|
_plus = {.x x, .y y, .c c, 0b r} _plus_;
|
|
53
|
-
_plus_ = ?{
|
|
55
|
+
_plus_ = ?{ bits[] = X x, ... }
|
|
54
56
|
<
|
|
55
57
|
{.x empty? if, .y empty? and, .c/0 c, .r inv then} .then,
|
|
56
58
|
{.x empty? if, .y empty? and, {.r x, 1 r} _inv then} .then,
|
|
@@ -70,7 +72,7 @@ _plus_ = ?{ <X 0, X 1, {} _>=X x, ... }
|
|
|
70
72
|
>;
|
|
71
73
|
|
|
72
74
|
_minus = {.x x, .y y, .c c, 0b r} _minus_;
|
|
73
|
-
_minus_ = ?{
|
|
75
|
+
_minus_ = ?{ bits[] = X x, ... }
|
|
74
76
|
<
|
|
75
77
|
{.y empty? if, .c/0 and, {.r x, .x r} _inv then} .then,
|
|
76
78
|
<
|
|
@@ -90,7 +92,7 @@ _minus_ = ?{ <X 0, X 1, {} _>=X x, ... }
|
|
|
90
92
|
plus_bits = {.x inv x, .y inv y, c0 c} _plus inv nat;
|
|
91
93
|
minus_bits = {.x inv x, .y inv y, c0 c} _minus inv nat;
|
|
92
94
|
|
|
93
|
-
_times = ?{
|
|
95
|
+
_times = ?{ bits[] = Y y, ... }
|
|
94
96
|
<
|
|
95
97
|
{.y empty? if, .result then} .then,
|
|
96
98
|
<
|
|
@@ -100,19 +102,20 @@ _times = ?{ <Y 0, Y 1, {} _>=Y y, ... }
|
|
|
100
102
|
>;
|
|
101
103
|
times_bits = {0 result, .x inv _x, .y y} _times inv nat;
|
|
102
104
|
|
|
103
|
-
unat_to_zeros =
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
unat_to_zeros =
|
|
106
|
+
? unat[] < /_ 0b, /1 unat_to_zeros |0 > ? bits[];
|
|
107
|
+
unat_to_double_zeros =
|
|
108
|
+
? unat[] < /_ 0b, /1 unat_to_double_zeros |0 |0 > ? bits[];
|
|
106
109
|
|
|
107
110
|
_peel2 = < </0,/1> </0,/1>, </0,/1>, () >;
|
|
108
|
-
_half_unat = ?{
|
|
111
|
+
_half_unat = ?{ bits[] = X x, ... }
|
|
109
112
|
<
|
|
110
113
|
{.x empty? if, .y empty? and, .m then} .then,
|
|
111
114
|
{.x _peel2 x, .y _peel2 y, .m |1 m} _half_unat
|
|
112
115
|
>;
|
|
113
116
|
half_unat = {.x x, .y y, 0u m} _half_unat;
|
|
114
117
|
|
|
115
|
-
_split_rev_u = ?{
|
|
118
|
+
_split_rev_u = ?{ bits[] = X x_rev, unat[] = M m, ... }
|
|
116
119
|
<
|
|
117
120
|
{.m /_ if, {.low nat low, .x_rev inv nat high} then} .then,
|
|
118
121
|
{.x_rev empty? if, {.low nat low, 0 high} then} .then,
|
|
@@ -155,7 +158,7 @@ shift2_bits_u =
|
|
|
155
158
|
{.val x, .m unat_to_double_zeros y} concat
|
|
156
159
|
>;
|
|
157
160
|
|
|
158
|
-
_karatsuba = ?{
|
|
161
|
+
_karatsuba = ?{ bits[] = X x, bits[] = Y y }
|
|
159
162
|
<
|
|
160
163
|
-- Base case: if m < 256 (< 512 bits), fall back to times_bits
|
|
161
164
|
{{.x x, .y y} half_unat m, .x x, .y y}
|
package/Examples/list.k
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
list
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
-- Examples/list.k: Generic linked list patterns and relations
|
|
2
|
+
|
|
3
|
+
list[X] = < {} nil, { X car, Y cdr } cons > = Y;
|
|
4
|
+
list? = ? list[X];
|
|
5
|
+
|
|
6
|
+
nil = {} | nil list?;
|
|
7
|
+
singleton = { () car, nil cdr } | cons list?;
|
|
8
|
+
cons = { .car car, .cdr cdr } | cons list?;
|
|
5
9
|
nil? = list? /nil nil;
|
|
6
10
|
car = list? /cons .car;
|
|
7
11
|
cdr = list? /cons .cdr;
|
package/Examples/nat.k
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
-- Examples/nat.k: Peano natural numbers arithmetic
|
|
2
|
+
|
|
3
|
+
0 = {} | 0;
|
|
4
|
+
0? = { /0 if, () then } .then;
|
|
5
|
+
succ = | +1;
|
|
6
|
+
pred = / +1;
|
|
7
|
+
|
|
8
|
+
nat[] = < {} 0, N +1 > = N;
|
|
9
|
+
nat? = ? nat[];
|
|
10
|
+
|
|
11
|
+
add = ?{ nat[] = N x, ... } = X <{.x pred x, .y succ y} ?X add, .y>;
|
|
12
|
+
mult = ?{ nat[] = M x, ... } <
|
|
13
|
+
{{.x pred x, .y y} mult x, .y y} add,
|
|
14
|
+
0
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
succ {()x, ()y} add
|
package/Examples/poly.k
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
-- Uses arbitrary-precision integers from Examples/arithmetics.k.
|
|
3
3
|
|
|
4
4
|
-- List constructors and destructors
|
|
5
|
-
list
|
|
5
|
+
list[X] = < {} nil, { X car, Y cdr } cons > = Y;
|
|
6
|
+
list? = ? list[X];
|
|
6
7
|
nil = {} | nil list?;
|
|
7
8
|
singleton = {() car, nil cdr} | cons list?;
|
|
8
9
|
cons = {.car car, .cdr cdr} | cons list?;
|
|
@@ -95,8 +96,15 @@ gt? =
|
|
|
95
96
|
split = {() xs, nil first, nil second} _split;
|
|
96
97
|
_split = <
|
|
97
98
|
{.xs /nil if, {.first first, .second second} then} .then,
|
|
98
|
-
{
|
|
99
|
-
|
|
99
|
+
{
|
|
100
|
+
.xs cdr /nil if,
|
|
101
|
+
{{.xs car car, .first cdr} cons first, .second second} then
|
|
102
|
+
} .then,
|
|
103
|
+
{
|
|
104
|
+
.xs cdr cdr xs,
|
|
105
|
+
{.xs car car, .first cdr} cons first,
|
|
106
|
+
{.xs cdr car car, .second cdr} cons second
|
|
107
|
+
} _split
|
|
100
108
|
>;
|
|
101
109
|
|
|
102
110
|
-- Merge two sorted lists: {xs, ys} -> list
|
package/Examples/vhdl_to_k.k
CHANGED
|
@@ -1,50 +1,62 @@
|
|
|
1
1
|
-- VHDL to K Translation Patterns
|
|
2
2
|
|
|
3
3
|
-- VHDL: signal a : std_logic;
|
|
4
|
-
-- K:
|
|
4
|
+
-- K: bit[] = < {} 0, {} 1 >;
|
|
5
|
+
bit[] = < {} 0, {} 1 >;
|
|
5
6
|
|
|
6
7
|
-- VHDL: signal bus : std_logic_vector(7 downto 0);
|
|
7
|
-
-- K:
|
|
8
|
+
-- K: byte[] = {
|
|
9
|
+
-- bit[] 0, bit[] 1, bit[] 2, bit[] 3,
|
|
10
|
+
-- bit[] 4, bit[] 5, bit[] 6, bit[] 7
|
|
11
|
+
-- };
|
|
12
|
+
byte[] = {
|
|
13
|
+
bit[] 0, bit[] 1, bit[] 2, bit[] 3,
|
|
14
|
+
bit[] 4, bit[] 5, bit[] 6, bit[] 7
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
nibble[] = { bit[] 0, bit[] 1, bit[] 2, bit[] 3 };
|
|
8
18
|
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
0 = {} | 0;
|
|
20
|
+
1 = {} | 1;
|
|
11
21
|
|
|
12
|
-
-- VHDL:
|
|
13
|
-
|
|
22
|
+
-- VHDL: y <= not a;
|
|
23
|
+
not_bit = < /0 1, /1 0 >;
|
|
14
24
|
|
|
15
25
|
-- VHDL: y <= a and b;
|
|
16
|
-
|
|
26
|
+
and_bit = < .x/0 0, .y/0 0, 1 >;
|
|
17
27
|
|
|
18
|
-
-- VHDL: y <= a or b;
|
|
19
|
-
|
|
28
|
+
-- VHDL: y <= a or b;
|
|
29
|
+
or_bit = < .x/1 1, .y/1 1, 0 >;
|
|
20
30
|
|
|
21
|
-
-- VHDL: y <=
|
|
22
|
-
|
|
31
|
+
-- VHDL: y <= a xor b;
|
|
32
|
+
xor_bit = < {.x/0 if, .y then} .then, .y not_bit >;
|
|
33
|
+
xor_abc = {{.a x, .b y} xor_bit x, .c y} xor_bit;
|
|
23
34
|
|
|
24
|
-
-- VHDL:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
-- VHDL: Full adder component
|
|
36
|
+
full_adder = ?{ bit[] a, bit[] b, bit[] c_in } {
|
|
37
|
+
{.a a, .b b, .c_in c} xor_abc sum,
|
|
38
|
+
{
|
|
39
|
+
{.a x, .b y} and_bit x,
|
|
40
|
+
{{.a x, .b y} xor_bit x, .c_in y} and_bit y
|
|
41
|
+
} or_bit carry
|
|
42
|
+
};
|
|
29
43
|
|
|
30
44
|
-- VHDL: architecture of 4-bit adder
|
|
31
|
-
four_bit_adder =
|
|
32
|
-
|
|
33
|
-
{
|
|
45
|
+
four_bit_adder = ?{ nibble[] a, nibble[] b } {
|
|
46
|
+
.a a, .b b,
|
|
47
|
+
{.a.0 a, .b.0 b, 0 c_in} full_adder fa0
|
|
48
|
+
} {
|
|
49
|
+
.a a, .b b, .fa0 fa0,
|
|
50
|
+
{.a.1 a, .b.1 b, .fa0.carry c_in} full_adder fa1
|
|
51
|
+
} {
|
|
52
|
+
.a a, .b b, .fa0 fa0, .fa1 fa1,
|
|
53
|
+
{.a.2 a, .b.2 b, .fa1.carry c_in} full_adder fa2
|
|
54
|
+
} {
|
|
55
|
+
.fa0 fa0, .fa1 fa1, .fa2 fa2,
|
|
56
|
+
{.a.3 a, .b.3 b, .fa2.carry c_in} full_adder fa3
|
|
34
57
|
} {
|
|
35
|
-
{.a.0 a0, .b.0 b0} full_adder fa0,
|
|
36
|
-
{.a.1 a1, .b.1 b1, .fa0.carry c_in} full_adder fa1,
|
|
37
|
-
{.a.2 a2, .b.2 b2, .fa1.carry c_in} full_adder fa2,
|
|
38
|
-
{.a.3 a3, .b.3 b3, .fa2.carry c_in} full_adder fa3,
|
|
39
58
|
{.fa0.sum 0, .fa1.sum 1, .fa2.sum 2, .fa3.sum 3} sum,
|
|
40
59
|
.fa3.carry overflow
|
|
41
60
|
};
|
|
42
61
|
|
|
43
|
-
|
|
44
|
-
full_adder = ${bit a, bit b, bit c_in} {
|
|
45
|
-
{.a a_val, .b b_val} xor_gate ab_xor,
|
|
46
|
-
{.ab_xor xor_val, .c_in c_val} xor_gate sum,
|
|
47
|
-
{.a a_val, .b b_val} and_gate ab_and,
|
|
48
|
-
{.ab_xor xor_val, .c_in c_val} and_gate xor_cin_and,
|
|
49
|
-
{.ab_and and1, .xor_cin_and and2} or_gate carry
|
|
50
|
-
};
|
|
62
|
+
()
|
package/README.md
CHANGED
|
@@ -45,18 +45,19 @@ k can define recursive data and transformations in the same file. Peano
|
|
|
45
45
|
natural numbers are either `0` or one more than another natural number:
|
|
46
46
|
|
|
47
47
|
```k
|
|
48
|
+
nat[] = < {} 0, N +1 > = N;
|
|
48
49
|
0 = {} | 0;
|
|
49
50
|
inc = | +1;
|
|
50
51
|
dec = / +1; # undefined for '0'
|
|
51
|
-
add = ?{
|
|
52
|
-
{ .
|
|
53
|
-
.
|
|
52
|
+
add = ?{ nat[] = N x, ... } <
|
|
53
|
+
{ .x dec x, .y inc y } add, # defined if 'x > 0'
|
|
54
|
+
.y # else, return 'y'
|
|
54
55
|
>;
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
`add` takes a product `{ x, y }`. If `x` has a `+1`, it moves that
|
|
58
59
|
successor from `x` to `y` and recurses. When that no longer applies, it
|
|
59
|
-
returns `y`. The inductive filter `?{
|
|
60
|
+
returns `y`. The inductive filter `?{ nat[] = N x, ... }`
|
|
60
61
|
explicitly witnesses that recursive calls shrink `x`.
|
|
61
62
|
|
|
62
63
|
In the REPL:
|
|
@@ -122,19 +123,41 @@ Every installed command supports `-h` and `--help`.
|
|
|
122
123
|
|
|
123
124
|
## What k Gives You
|
|
124
125
|
|
|
125
|
-
**Algebraic data shapes**
|
|
126
|
+
**Algebraic data shapes and named patterns**
|
|
126
127
|
|
|
127
|
-
All data structures in k are built from products and tagged unions
|
|
128
|
-
|
|
128
|
+
All data structures in k are built from products and tagged unions
|
|
129
|
+
(variants). Reusable named patterns describe structural type schemas
|
|
130
|
+
using square brackets `name[...]`:
|
|
129
131
|
|
|
130
132
|
```k
|
|
131
|
-
bit
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
bit[] = < {} 0, {} 1 >;
|
|
134
|
+
bits[] = < {} _, B 0, B 1 > = B;
|
|
135
|
+
byte[] = {
|
|
136
|
+
bit[] 0, bit[] 1, bit[] 2, bit[] 3,
|
|
137
|
+
bit[] 4, bit[] 5, bit[] 6, bit[] 7
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
-- Parameterized type templates:
|
|
141
|
+
list[X] = < {} nil, { X car, Y cdr } cons > = Y;
|
|
142
|
+
pair[A, B] = { A first, B second };
|
|
134
143
|
```
|
|
135
144
|
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
Callers instantiate patterns using `? name[...]` as standalone filters
|
|
146
|
+
or inside relation parameter guards (`?{ nat[] = N x, ... }`). There are
|
|
147
|
+
no built-in primitive values: the empty product `{}` is the only leaf
|
|
148
|
+
in a non-recursive definition.
|
|
149
|
+
|
|
150
|
+
**The four-bracket architecture**
|
|
151
|
+
|
|
152
|
+
k uses an orthogonal four-bracket convention with non-overlapping
|
|
153
|
+
semantic domains:
|
|
154
|
+
|
|
155
|
+
| Brackets | Domain | Purpose & Concrete Example |
|
|
156
|
+
| :--- | :--- | :--- |
|
|
157
|
+
| `{ ... }` | Products | Record construction: `{ f a, g b }`, `{}` |
|
|
158
|
+
| `< ... >` | Unions | Variant choice: `< f, g >`, `<>` |
|
|
159
|
+
| `( ... )` | Relations | Composition & identity: `(f g)`, `()` |
|
|
160
|
+
| `[ ... ]` | Patterns | Reusable templates: `list[X]`, `bit[]` |
|
|
138
161
|
|
|
139
162
|
**Composable partial functions**
|
|
140
163
|
|
|
@@ -149,6 +172,7 @@ Core expressions are deliberately small:
|
|
|
149
172
|
| `<f, g>` | try `f`, then `g` if `f` is undefined |
|
|
150
173
|
| `{f a, g b}` | build a product from parallel transformations |
|
|
151
174
|
| `?filter` | schema filter / inductive termination witness |
|
|
175
|
+
| `name[...]` | instantiate named pattern template |
|
|
152
176
|
| `()` | identity |
|
|
153
177
|
| `<>` | always undefined |
|
|
154
178
|
| `{}` | constant empty product |
|
|
@@ -223,10 +247,10 @@ For a detailed guide to all four formats, commands, and workflows, see
|
|
|
223
247
|
|
|
224
248
|
| Command | Purpose |
|
|
225
249
|
| --- | --- |
|
|
226
|
-
| `k-json` | Convert JSON to/from
|
|
227
|
-
| `k-djson` | Convert JSON to/from
|
|
228
|
-
| `k-int` | Convert decimal integers to/from
|
|
229
|
-
| `k-ieee` | Convert float literals to/from
|
|
250
|
+
| `k-json` | Convert JSON to/from binary stream (specializing) |
|
|
251
|
+
| `k-djson` | Convert JSON to/from dynamic JSON (binary bit-trie) |
|
|
252
|
+
| `k-int` | Convert decimal integers to/from binary stream |
|
|
253
|
+
| `k-ieee` | Convert float literals to/from binary stream |
|
|
230
254
|
| `k-unit` | Produce or validate the unit value |
|
|
231
255
|
| `k-utf8` / `k-utf16` | Convert text to/from k string streams |
|
|
232
256
|
|
package/browser/repl-browser.mjs
CHANGED
|
@@ -632,6 +632,8 @@ function renderCodecsModal() {
|
|
|
632
632
|
targetDesc = "{}";
|
|
633
633
|
} else if (entry.name === "json.mjs") {
|
|
634
634
|
targetDesc = "Self-describing JSON";
|
|
635
|
+
} else if (entry.name === "djson.mjs") {
|
|
636
|
+
targetDesc = "$ json (binary bit-trie)";
|
|
635
637
|
} else if (Array.isArray(entry.patterns) && entry.patterns.length > 0) {
|
|
636
638
|
targetDesc = `Pattern Family (${entry.patterns.length})`;
|
|
637
639
|
} else if (entry.pattern) {
|
package/browser/tree-view.mjs
CHANGED
|
@@ -455,7 +455,7 @@ export function createOutputEntryElement({
|
|
|
455
455
|
modeGroup.appendChild(rawBtn);
|
|
456
456
|
toolbar.appendChild(modeGroup);
|
|
457
457
|
|
|
458
|
-
// Tree Action buttons group (Expand / Collapse
|
|
458
|
+
// Tree Action buttons group (Expand / Collapse)
|
|
459
459
|
const actionGroup = document.createElement("div");
|
|
460
460
|
actionGroup.className = "k-action-group";
|
|
461
461
|
|
|
@@ -471,23 +471,8 @@ export function createOutputEntryElement({
|
|
|
471
471
|
collapseAllBtn.innerHTML = `⊟ Collapse`;
|
|
472
472
|
collapseAllBtn.title = "Reset trees to top 3 levels";
|
|
473
473
|
|
|
474
|
-
const copyBtn = document.createElement("button");
|
|
475
|
-
copyBtn.type = "button";
|
|
476
|
-
copyBtn.className = "k-btn-action";
|
|
477
|
-
copyBtn.innerHTML = `📋 Copy`;
|
|
478
|
-
copyBtn.title = "Copy raw output to clipboard";
|
|
479
|
-
copyBtn.onclick = (e) => {
|
|
480
|
-
e.stopPropagation();
|
|
481
|
-
navigator.clipboard.writeText(rawText);
|
|
482
|
-
copyBtn.innerHTML = `✓ Copied`;
|
|
483
|
-
setTimeout(() => {
|
|
484
|
-
copyBtn.innerHTML = `📋 Copy`;
|
|
485
|
-
}, 1500);
|
|
486
|
-
};
|
|
487
|
-
|
|
488
474
|
actionGroup.appendChild(expandAllBtn);
|
|
489
475
|
actionGroup.appendChild(collapseAllBtn);
|
|
490
|
-
actionGroup.appendChild(copyBtn);
|
|
491
476
|
toolbar.appendChild(actionGroup);
|
|
492
477
|
|
|
493
478
|
container.appendChild(toolbar);
|
|
@@ -512,6 +497,23 @@ export function createOutputEntryElement({
|
|
|
512
497
|
`<span class="k-tree-arrow">▶</span> ` +
|
|
513
498
|
`<span class="k-section-title">🌲 Value</span> ` +
|
|
514
499
|
`<span class="k-section-preview">${valEscaped}</span>`;
|
|
500
|
+
|
|
501
|
+
const copyValBtn = document.createElement("button");
|
|
502
|
+
copyValBtn.type = "button";
|
|
503
|
+
copyValBtn.className = "k-btn-action k-btn-copy k-btn-copy-value";
|
|
504
|
+
copyValBtn.innerHTML = `📋 Copy`;
|
|
505
|
+
copyValBtn.title = "Copy value definition to clipboard";
|
|
506
|
+
copyValBtn.onclick = (e) => {
|
|
507
|
+
e.preventDefault();
|
|
508
|
+
e.stopPropagation();
|
|
509
|
+
const valStr = valueToK(value);
|
|
510
|
+
navigator.clipboard.writeText(valStr);
|
|
511
|
+
copyValBtn.innerHTML = `✓ Copied`;
|
|
512
|
+
setTimeout(() => {
|
|
513
|
+
copyValBtn.innerHTML = `📋 Copy`;
|
|
514
|
+
}, 1500);
|
|
515
|
+
};
|
|
516
|
+
valSummary.appendChild(copyValBtn);
|
|
515
517
|
valSection.appendChild(valSummary);
|
|
516
518
|
|
|
517
519
|
const valBody = document.createElement("div");
|
|
@@ -538,6 +540,23 @@ export function createOutputEntryElement({
|
|
|
538
540
|
`<span class="k-tree-arrow">▶</span> ` +
|
|
539
541
|
`<span class="k-section-title">🌿 Pattern</span> ` +
|
|
540
542
|
`<span class="k-section-preview">?${patEscaped}</span>`;
|
|
543
|
+
|
|
544
|
+
const copyPatBtn = document.createElement("button");
|
|
545
|
+
copyPatBtn.type = "button";
|
|
546
|
+
copyPatBtn.className = "k-btn-action k-btn-copy k-btn-copy-pattern";
|
|
547
|
+
copyPatBtn.innerHTML = `📋 Copy`;
|
|
548
|
+
copyPatBtn.title = "Copy pattern definition to clipboard";
|
|
549
|
+
copyPatBtn.onclick = (e) => {
|
|
550
|
+
e.preventDefault();
|
|
551
|
+
e.stopPropagation();
|
|
552
|
+
const patStr = `?${propertyListToFilter(value.pattern, "X")}`;
|
|
553
|
+
navigator.clipboard.writeText(patStr);
|
|
554
|
+
copyPatBtn.innerHTML = `✓ Copied`;
|
|
555
|
+
setTimeout(() => {
|
|
556
|
+
copyPatBtn.innerHTML = `📋 Copy`;
|
|
557
|
+
}, 1500);
|
|
558
|
+
};
|
|
559
|
+
patSummary.appendChild(copyPatBtn);
|
|
541
560
|
patSection.appendChild(patSummary);
|
|
542
561
|
|
|
543
562
|
patBody = document.createElement("div");
|