@fraczak/k 6.7.0 → 6.8.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/DOCS/REPL.md +23 -36
- package/TODO.md +112 -0
- package/browser/repl-browser.mjs +146 -94
- package/browser/vfs-data.mjs +2 -0
- package/package.json +1 -1
- package/repl-codecs.mjs +26 -5
- package/repl.mjs +214 -148
- package/scripts/build-repl-html.mjs +213 -72
- package/tests/test-repl-html.mjs +84 -6
- package/tests/test-repl.mjs +90 -40
|
@@ -28,8 +28,8 @@ if (fs.existsSync(coreKPath)) {
|
|
|
28
28
|
vfsFiles["core.k"] = fs.readFileSync(coreKPath, "utf8");
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// Add verified k-code files: arithmetics.k, ieee.k
|
|
32
|
-
const verifiedExamples = ["arithmetics.k", "ieee.k"];
|
|
31
|
+
// Add verified k-code files: arithmetics.k, ieee.k, poly.k
|
|
32
|
+
const verifiedExamples = ["arithmetics.k", "ieee.k", "poly.k"];
|
|
33
33
|
const examplesDir = path.join(projectRoot, "Examples");
|
|
34
34
|
for (const filename of verifiedExamples) {
|
|
35
35
|
const fullPath = path.join(examplesDir, filename);
|
|
@@ -173,21 +173,24 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
173
173
|
|
|
174
174
|
/* Header */
|
|
175
175
|
.navbar {
|
|
176
|
-
height: 48px;
|
|
176
|
+
min-height: 48px;
|
|
177
|
+
height: auto;
|
|
177
178
|
background: var(--bg-surface);
|
|
178
179
|
border-bottom: 1px solid var(--border-color);
|
|
179
180
|
display: flex;
|
|
180
181
|
align-items: center;
|
|
181
182
|
justify-content: space-between;
|
|
182
|
-
padding:
|
|
183
|
+
padding: 6px 16px;
|
|
183
184
|
flex-shrink: 0;
|
|
184
|
-
|
|
185
|
+
flex-wrap: wrap;
|
|
186
|
+
gap: 8px 12px;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
.nav-left {
|
|
188
190
|
display: flex;
|
|
189
191
|
align-items: center;
|
|
190
192
|
gap: 12px;
|
|
193
|
+
flex-shrink: 0;
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
.brand {
|
|
@@ -199,12 +202,6 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
199
202
|
user-select: none;
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
.brand-lambda {
|
|
203
|
-
color: var(--color-prompt);
|
|
204
|
-
font-size: 20px;
|
|
205
|
-
line-height: 1;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
205
|
.brand-name {
|
|
209
206
|
letter-spacing: 0.5px;
|
|
210
207
|
}
|
|
@@ -253,8 +250,11 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
253
250
|
.nav-right {
|
|
254
251
|
display: flex;
|
|
255
252
|
align-items: center;
|
|
256
|
-
gap: 8px;
|
|
257
|
-
flex-wrap:
|
|
253
|
+
gap: 6px 8px;
|
|
254
|
+
flex-wrap: wrap;
|
|
255
|
+
justify-content: flex-end;
|
|
256
|
+
flex: 1 1 auto;
|
|
257
|
+
min-width: 0;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
.control-group {
|
|
@@ -320,7 +320,8 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
320
320
|
|
|
321
321
|
/* Terminal */
|
|
322
322
|
.terminal-main {
|
|
323
|
-
flex: 1;
|
|
323
|
+
flex: 1 1 auto;
|
|
324
|
+
min-height: 0;
|
|
324
325
|
display: flex;
|
|
325
326
|
flex-direction: column;
|
|
326
327
|
overflow: hidden;
|
|
@@ -758,7 +759,7 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
758
759
|
|
|
759
760
|
/* Welcome banner */
|
|
760
761
|
.welcome-banner {
|
|
761
|
-
padding: 16px;
|
|
762
|
+
padding: 12px 16px;
|
|
762
763
|
background: var(--bg-surface);
|
|
763
764
|
border: 1px solid var(--border-color);
|
|
764
765
|
border-radius: 8px;
|
|
@@ -766,10 +767,10 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
766
767
|
}
|
|
767
768
|
|
|
768
769
|
.welcome-title {
|
|
769
|
-
font-size:
|
|
770
|
+
font-size: 16px;
|
|
770
771
|
font-weight: 700;
|
|
771
772
|
color: var(--color-prompt);
|
|
772
|
-
margin-bottom:
|
|
773
|
+
margin-bottom: 0;
|
|
773
774
|
}
|
|
774
775
|
|
|
775
776
|
.welcome-desc {
|
|
@@ -990,6 +991,17 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
990
991
|
font-size: 13px;
|
|
991
992
|
}
|
|
992
993
|
|
|
994
|
+
.help-table th {
|
|
995
|
+
text-align: left;
|
|
996
|
+
padding: 6px 10px;
|
|
997
|
+
font-size: 11px;
|
|
998
|
+
font-weight: 600;
|
|
999
|
+
color: var(--text-muted);
|
|
1000
|
+
text-transform: uppercase;
|
|
1001
|
+
letter-spacing: 0.5px;
|
|
1002
|
+
border-bottom: 1px solid var(--border-color);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
993
1005
|
.help-table td {
|
|
994
1006
|
padding: 6px 10px;
|
|
995
1007
|
border-bottom: 1px solid rgba(48, 54, 61, 0.4);
|
|
@@ -1001,6 +1013,27 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1001
1013
|
color: var(--color-cyan);
|
|
1002
1014
|
}
|
|
1003
1015
|
|
|
1016
|
+
.help-bridge-box {
|
|
1017
|
+
background: rgba(88, 166, 255, 0.08);
|
|
1018
|
+
border: 1px solid rgba(88, 166, 255, 0.25);
|
|
1019
|
+
border-left: 3px solid var(--color-prompt);
|
|
1020
|
+
border-radius: 4px;
|
|
1021
|
+
padding: 10px 14px;
|
|
1022
|
+
margin: 12px 0 16px 0;
|
|
1023
|
+
font-size: 12px;
|
|
1024
|
+
line-height: 1.5;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
.help-category-title {
|
|
1028
|
+
font-size: 13px;
|
|
1029
|
+
font-weight: 600;
|
|
1030
|
+
color: var(--text-main);
|
|
1031
|
+
margin: 16px 0 6px 0;
|
|
1032
|
+
display: flex;
|
|
1033
|
+
align-items: center;
|
|
1034
|
+
gap: 6px;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1004
1037
|
.help-section ul {
|
|
1005
1038
|
padding-left: 24px;
|
|
1006
1039
|
font-size: 13px;
|
|
@@ -1312,6 +1345,12 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1312
1345
|
border-color: rgba(210, 153, 34, 0.3);
|
|
1313
1346
|
}
|
|
1314
1347
|
|
|
1348
|
+
.badge-type.parameterized {
|
|
1349
|
+
background: rgba(187, 128, 179, 0.15);
|
|
1350
|
+
color: var(--color-purple);
|
|
1351
|
+
border-color: rgba(187, 128, 179, 0.3);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1315
1354
|
.codecs-table-container {
|
|
1316
1355
|
background: var(--bg-base);
|
|
1317
1356
|
border: 1px solid var(--border-color);
|
|
@@ -1600,14 +1639,27 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1600
1639
|
/* Responsive */
|
|
1601
1640
|
@media (max-width: 768px) {
|
|
1602
1641
|
.navbar {
|
|
1603
|
-
height: auto;
|
|
1604
|
-
flex-direction: column;
|
|
1605
1642
|
padding: 8px 12px;
|
|
1606
|
-
|
|
1643
|
+
gap: 8px;
|
|
1644
|
+
}
|
|
1645
|
+
.nav-left {
|
|
1646
|
+
width: 100%;
|
|
1647
|
+
justify-content: space-between;
|
|
1607
1648
|
}
|
|
1608
1649
|
.nav-right {
|
|
1609
|
-
|
|
1610
|
-
|
|
1650
|
+
width: 100%;
|
|
1651
|
+
justify-content: flex-start;
|
|
1652
|
+
overflow-x: visible;
|
|
1653
|
+
flex-wrap: wrap;
|
|
1654
|
+
gap: 6px;
|
|
1655
|
+
}
|
|
1656
|
+
.control-group {
|
|
1657
|
+
flex: 1 1 180px;
|
|
1658
|
+
}
|
|
1659
|
+
.control-group select {
|
|
1660
|
+
flex: 1 1 auto;
|
|
1661
|
+
max-width: 100%;
|
|
1662
|
+
min-width: 120px;
|
|
1611
1663
|
}
|
|
1612
1664
|
.vfs-body {
|
|
1613
1665
|
flex-direction: column;
|
|
@@ -1627,7 +1679,6 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1627
1679
|
<header class="navbar">
|
|
1628
1680
|
<div class="nav-left">
|
|
1629
1681
|
<div class="brand">
|
|
1630
|
-
<span class="brand-lambda">λ</span>
|
|
1631
1682
|
<span class="brand-name">k repl</span>
|
|
1632
1683
|
<span class="engine-badge">wasm-in-process</span>
|
|
1633
1684
|
</div>
|
|
@@ -1646,27 +1697,27 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1646
1697
|
<option value="core.k">core.k</option>
|
|
1647
1698
|
<option value="arithmetics.k">arithmetics.k</option>
|
|
1648
1699
|
<option value="ieee.k">ieee.k</option>
|
|
1700
|
+
<option value="poly.k">poly.k</option>
|
|
1649
1701
|
</optgroup>
|
|
1650
1702
|
<optgroup label="Quick Expressions">
|
|
1651
|
-
<option value="expr:
|
|
1652
|
-
<option value="expr:
|
|
1703
|
+
<option value="expr:{} | ok">{} | ok</option>
|
|
1704
|
+
<option value="expr:()">()</option>
|
|
1705
|
+
<option value="expr:$ bool = < {} true, {} false >; not = $ bool < / true | false, {} | true > $ bool; {} | true not">not (boolean relation with fallback)</option>
|
|
1706
|
+
<option value="expr:{10 x, 5 y} plus">{10 x, 5 y} plus (bits)</option>
|
|
1653
1707
|
<option value="expr:1 succ">1 succ</option>
|
|
1654
|
-
<option value="expr:
|
|
1655
|
-
<option value="expr::
|
|
1656
|
-
<option value="expr::
|
|
1657
|
-
<option value="expr::codes">:codes</option>
|
|
1658
|
-
<option value="expr::rels">:rels</option>
|
|
1659
|
-
<option value="expr::val">:val</option>
|
|
1708
|
+
<option value="expr:{10 int x, 5 int y} plus">{10 int x, 5 int y} plus (int)</option>
|
|
1709
|
+
<option value="expr::codes">:codes (list types)</option>
|
|
1710
|
+
<option value="expr::rels">:rels (list relations)</option>
|
|
1660
1711
|
</optgroup>
|
|
1661
|
-
<optgroup label="Codecs &
|
|
1712
|
+
<optgroup label="Codecs & Format Adapters">
|
|
1662
1713
|
<option value="expr::codecs">:codecs</option>
|
|
1663
|
-
<option value="expr::input">:input (
|
|
1714
|
+
<option value="expr::input">:input (interactive dialog)</option>
|
|
1664
1715
|
<option value="expr::codec load int">:codec load int</option>
|
|
1665
|
-
<option value="expr:10 int">10 int</option>
|
|
1666
1716
|
<option value="expr::codec load utf8">:codec load utf8</option>
|
|
1667
1717
|
<option value="expr::codec load json">:codec load json</option>
|
|
1668
1718
|
<option value="expr::codec load ieee">:codec load ieee</option>
|
|
1669
1719
|
<option value="expr::codec load unit">:codec load unit</option>
|
|
1720
|
+
<option value="expr::input {float64 x, string n} json">:input {float64 x, string n} json</option>
|
|
1670
1721
|
</optgroup>
|
|
1671
1722
|
</select>
|
|
1672
1723
|
<button id="btn-load-example" class="btn btn-primary">Load</button>
|
|
@@ -1690,9 +1741,6 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1690
1741
|
<!-- Clear -->
|
|
1691
1742
|
<button id="btn-clear" class="btn btn-secondary" title="Clear terminal screen (Ctrl+L)">⌫ Clear</button>
|
|
1692
1743
|
|
|
1693
|
-
<!-- Reset -->
|
|
1694
|
-
<button id="btn-reset" class="btn btn-secondary" title="Reset REPL state (:reset)">↺ Reset</button>
|
|
1695
|
-
|
|
1696
1744
|
<!-- Export -->
|
|
1697
1745
|
<button id="btn-export" class="btn btn-secondary" title="Export session to .klib">💾 Save .klib</button>
|
|
1698
1746
|
|
|
@@ -1757,17 +1805,17 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1757
1805
|
<div class="modal-dialog codecs-dialog">
|
|
1758
1806
|
<div class="modal-header">
|
|
1759
1807
|
<div style="display: flex; align-items: center; gap: 10px;">
|
|
1760
|
-
<h3>Codecs & Custom Serializers / Deserializers</h3>
|
|
1808
|
+
<h3>Format Codecs & Custom Serializers / Deserializers</h3>
|
|
1761
1809
|
<span class="engine-badge">:codecs subsystem</span>
|
|
1762
1810
|
</div>
|
|
1763
1811
|
<button id="btn-close-codecs" class="btn-close">×</button>
|
|
1764
1812
|
</div>
|
|
1765
1813
|
<div class="modal-body codecs-body">
|
|
1766
|
-
<!-- Section 1:
|
|
1814
|
+
<!-- Section 1: Available Format Codecs -->
|
|
1767
1815
|
<div class="codecs-section">
|
|
1768
1816
|
<div class="codecs-section-header">
|
|
1769
|
-
<h4>
|
|
1770
|
-
<span class="section-subtitle">Click 'Load' to activate
|
|
1817
|
+
<h4>Available Format Codecs</h4>
|
|
1818
|
+
<span class="section-subtitle">External adapters translating between text formats and typed <code>(filter, tree)</code> values. Click 'Load' to activate in current session.</span>
|
|
1771
1819
|
</div>
|
|
1772
1820
|
<div class="codec-grid">
|
|
1773
1821
|
<!-- int card -->
|
|
@@ -1778,8 +1826,8 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1778
1826
|
<span class="codec-name">int</span>
|
|
1779
1827
|
<span id="codec-badge-int" class="codec-status-badge">Inactive</span>
|
|
1780
1828
|
</div>
|
|
1781
|
-
<div class="codec-types">Target: <span class="badge-type">$int</span></div>
|
|
1782
|
-
<div class="codec-desc">
|
|
1829
|
+
<div class="codec-types">Target: <span class="badge-type">$ int</span></div>
|
|
1830
|
+
<div class="codec-desc">Converts decimal integers to/from <code>$ int = < bits '+', bits '-' ></code>.</div>
|
|
1783
1831
|
</div>
|
|
1784
1832
|
<div class="codec-card-footer">
|
|
1785
1833
|
<div class="codec-caps">
|
|
@@ -1798,8 +1846,8 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1798
1846
|
<span class="codec-name">utf8</span>
|
|
1799
1847
|
<span id="codec-badge-utf8" class="codec-status-badge">Inactive</span>
|
|
1800
1848
|
</div>
|
|
1801
|
-
<div class="codec-types">Target: <span class="badge-type">$
|
|
1802
|
-
<div class="codec-desc">
|
|
1849
|
+
<div class="codec-types">Target: <span class="badge-type">$ string ($ utf8)</span></div>
|
|
1850
|
+
<div class="codec-desc">Converts text to/from <code>$ string</code>, a list of Unicode scalar values (<code>$ unicode</code>) partitioned by Unicode planes.</div>
|
|
1803
1851
|
</div>
|
|
1804
1852
|
<div class="codec-card-footer">
|
|
1805
1853
|
<div class="codec-caps">
|
|
@@ -1818,8 +1866,8 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1818
1866
|
<span class="codec-name">json</span>
|
|
1819
1867
|
<span id="codec-badge-json" class="codec-status-badge">Inactive</span>
|
|
1820
1868
|
</div>
|
|
1821
|
-
<div class="codec-types">Target: <span class="badge-type
|
|
1822
|
-
<div class="codec-desc">
|
|
1869
|
+
<div class="codec-types">Target: <span class="badge-type parameterized">Pattern / Universal</span></div>
|
|
1870
|
+
<div class="codec-desc">Universal / pattern-driven codec. Deserializes JSON text into <i>k</i> values using a pattern expression (e.g. <code>:input ? {float64 x, string n} json</code> or <code>:input json</code>), or serializes <i>k</i> values to JSON.</div>
|
|
1823
1871
|
</div>
|
|
1824
1872
|
<div class="codec-card-footer">
|
|
1825
1873
|
<div class="codec-caps">
|
|
@@ -1838,8 +1886,8 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1838
1886
|
<span class="codec-name">ieee</span>
|
|
1839
1887
|
<span id="codec-badge-ieee" class="codec-status-badge">Inactive</span>
|
|
1840
1888
|
</div>
|
|
1841
|
-
<div class="codec-types">Target: <span class="badge-type">$
|
|
1842
|
-
<div class="codec-desc">IEEE 754 floating point numbers serialized to sign, exponent, and
|
|
1889
|
+
<div class="codec-types">Target: <span class="badge-type">$ float64</span></div>
|
|
1890
|
+
<div class="codec-desc">IEEE 754 floating point numbers serialized to/from sign, exponent, and fraction fields.</div>
|
|
1843
1891
|
</div>
|
|
1844
1892
|
<div class="codec-card-footer">
|
|
1845
1893
|
<div class="codec-caps">
|
|
@@ -1880,7 +1928,7 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1880
1928
|
<div class="codecs-section">
|
|
1881
1929
|
<div class="codecs-section-header">
|
|
1882
1930
|
<h4>Custom Serializer & Deserializer Studio</h4>
|
|
1883
|
-
<span class="section-subtitle">
|
|
1931
|
+
<span class="section-subtitle">Codecs convert external text representations to/from <i>k</i> values defined as pairs <code>(filter, tree)</code>.</span>
|
|
1884
1932
|
</div>
|
|
1885
1933
|
|
|
1886
1934
|
<div class="codec-studio-box">
|
|
@@ -1888,9 +1936,9 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1888
1936
|
<div class="studio-field">
|
|
1889
1937
|
<label for="codec-preset-select">Preset Template:</label>
|
|
1890
1938
|
<select id="codec-preset-select">
|
|
1891
|
-
<option value="yn">Yes / No ($
|
|
1892
|
-
<option value="hex">Hexadecimal 0x... ($int)</option>
|
|
1893
|
-
<option value="currency">Currency $XX.YY ($int cents)</option>
|
|
1939
|
+
<option value="yn">Yes / No ($ yes_no)</option>
|
|
1940
|
+
<option value="hex">Hexadecimal 0x... ($ int)</option>
|
|
1941
|
+
<option value="currency">Currency $XX.YY ($ int cents)</option>
|
|
1894
1942
|
<option value="blank">Blank / Custom</option>
|
|
1895
1943
|
</select>
|
|
1896
1944
|
</div>
|
|
@@ -1932,7 +1980,7 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1932
1980
|
<input type="text" id="codec-test-input" placeholder="e.g. yes or 0xFF or $42.50" value="yes">
|
|
1933
1981
|
</div>
|
|
1934
1982
|
<button id="btn-test-parse" class="btn btn-secondary btn-sm" title="Test parse function against test input">🧪 Test Parse</button>
|
|
1935
|
-
<button id="btn-test-print" class="btn btn-secondary btn-sm" title="Test print function against current REPL value
|
|
1983
|
+
<button id="btn-test-print" class="btn btn-secondary btn-sm" title="Test print function against current REPL value">🖨 Test Print</button>
|
|
1936
1984
|
<button id="btn-register-codec" class="btn btn-primary btn-sm" title="Register codec in active REPL state">✓ Register in REPL</button>
|
|
1937
1985
|
<button id="btn-save-codec-vfs" class="btn btn-secondary btn-sm" title="Export as standalone .mjs file to VFS">💾 Save to VFS</button>
|
|
1938
1986
|
</div>
|
|
@@ -1949,28 +1997,28 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
1949
1997
|
<div class="modal-dialog input-popup-dialog">
|
|
1950
1998
|
<div class="modal-header">
|
|
1951
1999
|
<div style="display: flex; align-items: center; gap: 10px;">
|
|
1952
|
-
<h3>📥
|
|
2000
|
+
<h3>📥 Code Input</h3>
|
|
1953
2001
|
<span class="engine-badge">:input directive</span>
|
|
1954
2002
|
</div>
|
|
1955
2003
|
<button id="btn-close-input-popup" class="btn-close">×</button>
|
|
1956
2004
|
</div>
|
|
1957
2005
|
<div class="modal-body input-popup-body">
|
|
1958
2006
|
<div id="input-popup-banner" class="input-popup-banner">
|
|
1959
|
-
<span id="input-popup-hint">Enter input string to deserialize into a K
|
|
2007
|
+
<span id="input-popup-hint">Enter input string to deserialize into a K value:</span>
|
|
1960
2008
|
</div>
|
|
1961
2009
|
|
|
1962
2010
|
<div class="input-popup-form-row">
|
|
1963
2011
|
<div class="studio-field" style="flex: 1;">
|
|
1964
|
-
<label for="input-popup-type">Target
|
|
1965
|
-
<
|
|
1966
|
-
|
|
1967
|
-
</select>
|
|
2012
|
+
<label for="input-popup-type">Target Pattern:</label>
|
|
2013
|
+
<input type="text" id="input-popup-type" list="input-popup-type-list" placeholder="e.g. (...) or $ int or ? {float64 x, string n}" autocomplete="off">
|
|
2014
|
+
<datalist id="input-popup-type-list"></datalist>
|
|
1968
2015
|
</div>
|
|
1969
2016
|
<div class="studio-field" style="flex: 1;">
|
|
1970
2017
|
<label for="input-popup-codec">Deserializer Codec:</label>
|
|
1971
2018
|
<select id="input-popup-codec">
|
|
1972
2019
|
<!-- populated dynamically -->
|
|
1973
2020
|
</select>
|
|
2021
|
+
<div id="input-popup-codec-hint" class="input-popup-codec-hint" style="font-size:11px;color:var(--text-muted);margin-top:4px;"></div>
|
|
1974
2022
|
</div>
|
|
1975
2023
|
</div>
|
|
1976
2024
|
|
|
@@ -2005,22 +2053,112 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
2005
2053
|
<button id="btn-close-help" class="btn-close">×</button>
|
|
2006
2054
|
</div>
|
|
2007
2055
|
<div class="modal-body help-body">
|
|
2056
|
+
<div class="help-section">
|
|
2057
|
+
<h4>Language Overview</h4>
|
|
2058
|
+
<p style="margin-bottom: 8px; color: var(--text-muted); line-height: 1.45;">
|
|
2059
|
+
<b>k</b> is a concise language of <b>types</b> and <b>relations</b> (functions) over tree-like (JSON-like) documents. Every expression denotes a relation that transforms an input document into an output document.
|
|
2060
|
+
</p>
|
|
2061
|
+
<p style="margin-bottom: 10px; color: var(--text-muted); line-height: 1.45;">
|
|
2062
|
+
<b>Content-Addressed:</b> Functions and types are identified by their canonical content hash (<code>@...</code>) derived directly from their structure. Local names are just temporary aliases used for recursion and readability.
|
|
2063
|
+
</p>
|
|
2064
|
+
|
|
2065
|
+
<div class="help-category-title">1. Definitions (Local Aliases)</div>
|
|
2066
|
+
<p style="margin-bottom: 6px; color: var(--text-muted); font-size: 12px;">Definitions introduce local aliases for recursion and convenience (terminated by <code>;</code>):</p>
|
|
2067
|
+
<table class="help-table">
|
|
2068
|
+
<thead>
|
|
2069
|
+
<tr><th style="width: 38%;">Syntax</th><th>Description & Example</th></tr>
|
|
2070
|
+
</thead>
|
|
2071
|
+
<tbody>
|
|
2072
|
+
<tr>
|
|
2073
|
+
<td><code>name = relExpr;</code></td>
|
|
2074
|
+
<td><b>Relation Alias:</b> Defines a local alias for a relation expression.<br><i>e.g.</i> <code>swap = { . y x, . x y };</code></td>
|
|
2075
|
+
</tr>
|
|
2076
|
+
<tr>
|
|
2077
|
+
<td><code>$ name = typeExpr;</code></td>
|
|
2078
|
+
<td><b>Type Alias:</b> Defines a local alias for a type schema.<br><i>e.g.</i> <code>$ bool = < {} true, {} false >;</code></td>
|
|
2079
|
+
</tr>
|
|
2080
|
+
</tbody>
|
|
2081
|
+
</table>
|
|
2082
|
+
|
|
2083
|
+
<div class="help-category-title">2. Type Expressions (<code>typeExpr</code>)</div>
|
|
2084
|
+
<p style="margin-bottom: 6px; color: var(--text-muted); font-size: 12px;">Schemas describing tree-like documents:</p>
|
|
2085
|
+
<table class="help-table">
|
|
2086
|
+
<thead>
|
|
2087
|
+
<tr><th style="width: 45%;">Syntax</th><th>Description</th></tr>
|
|
2088
|
+
</thead>
|
|
2089
|
+
<tbody>
|
|
2090
|
+
<tr>
|
|
2091
|
+
<td><code>{ Type_1 label_1, ..., Type_k label_k }</code></td>
|
|
2092
|
+
<td><b>Product Type:</b> Labeled record of arity <i>k</i> (e.g. <code>{float64 x, string n}</code>; special case: <code>{}</code> is the 0-ary product)</td>
|
|
2093
|
+
</tr>
|
|
2094
|
+
<tr>
|
|
2095
|
+
<td><code>< Type_1 tag_1, ..., Type_k tag_k ></code></td>
|
|
2096
|
+
<td><b>Union Type:</b> Tagged sum of <i>k</i> variants (e.g. <code><int ok, string err></code>)</td>
|
|
2097
|
+
</tr>
|
|
2098
|
+
<tr>
|
|
2099
|
+
<td><code>name</code> / <code>@hash</code></td>
|
|
2100
|
+
<td><b>Type Reference:</b> References a type by local alias or canonical content hash</td>
|
|
2101
|
+
</tr>
|
|
2102
|
+
</tbody>
|
|
2103
|
+
</table>
|
|
2104
|
+
|
|
2105
|
+
<div class="help-category-title">3. Relation Expressions (<code>relExpr</code>)</div>
|
|
2106
|
+
<p style="margin-bottom: 6px; color: var(--text-muted); font-size: 12px;">Functions transforming an input document to an output document:</p>
|
|
2107
|
+
<table class="help-table">
|
|
2108
|
+
<thead>
|
|
2109
|
+
<tr><th style="width: 45%;">Syntax</th><th>Description</th></tr>
|
|
2110
|
+
</thead>
|
|
2111
|
+
<tbody>
|
|
2112
|
+
<tr>
|
|
2113
|
+
<td><code>{ rel_1 label_1, ..., rel_k label_k }</code></td>
|
|
2114
|
+
<td><b>Product:</b> Evaluates <i>k</i> relations into record fields (special case: <code>{}</code> evaluates 0 relations, producing empty record <code>{}</code>)</td>
|
|
2115
|
+
</tr>
|
|
2116
|
+
<tr>
|
|
2117
|
+
<td><code>< rel_1, ..., rel_k ></code></td>
|
|
2118
|
+
<td><b>Union (Choice):</b> Evaluates relations in order of arity <i>k</i> (tries <code>rel_1</code>; if it fails, tries <code>rel_2</code>, ..., <code>rel_k</code>)</td>
|
|
2119
|
+
</tr>
|
|
2120
|
+
<tr>
|
|
2121
|
+
<td><code>( rel_1 ... rel_k )</code></td>
|
|
2122
|
+
<td><b>Composition:</b> Sequentially composes <i>k</i> relations (special case: <code>()</code> is the 0-ary composition, denoting identity; parentheses may be omitted when non-empty: <code>f g</code>)</td>
|
|
2123
|
+
</tr>
|
|
2124
|
+
<tr>
|
|
2125
|
+
<td><code>. label</code></td>
|
|
2126
|
+
<td><b>Field Projection:</b> Extracts field <code>label</code> from a record</td>
|
|
2127
|
+
</tr>
|
|
2128
|
+
<tr>
|
|
2129
|
+
<td><code>/ tag</code></td>
|
|
2130
|
+
<td><b>Variant Branch:</b> Extracts payload of variant <code>tag</code> (fails if tag differs)</td>
|
|
2131
|
+
</tr>
|
|
2132
|
+
<tr>
|
|
2133
|
+
<td><code>| tag</code></td>
|
|
2134
|
+
<td><b>Variant Constructor:</b> Wraps document into variant <code>tag</code></td>
|
|
2135
|
+
</tr>
|
|
2136
|
+
<tr>
|
|
2137
|
+
<td><code>$ typeExpr</code></td>
|
|
2138
|
+
<td><b>Type Constraint:</b> Asserts input matches <code>typeExpr</code> (compile-time Type Error on mismatch)</td>
|
|
2139
|
+
</tr>
|
|
2140
|
+
<tr>
|
|
2141
|
+
<td><code>name</code> / <code>@hash</code></td>
|
|
2142
|
+
<td><b>Relation Reference:</b> Calls a relation by local alias or canonical content hash</td>
|
|
2143
|
+
</tr>
|
|
2144
|
+
</tbody>
|
|
2145
|
+
</table>
|
|
2146
|
+
</div>
|
|
2147
|
+
|
|
2008
2148
|
<div class="help-section">
|
|
2009
2149
|
<h4>REPL Commands</h4>
|
|
2010
2150
|
<table class="help-table">
|
|
2011
2151
|
<tr><td><code>:load <file></code></td><td>Load a <code>.k</code> source or <code>.klib</code> library into current state</td></tr>
|
|
2012
2152
|
<tr><td><code>:t <name></code></td><td>Display the derived type of relation <code><name></code></td></tr>
|
|
2013
2153
|
<tr><td><code>:d <name></code></td><td>Display definition of relation <code><name></code></td></tr>
|
|
2014
|
-
<tr><td><code>:type <name></code></td><td>
|
|
2015
|
-
<tr><td><code>:
|
|
2016
|
-
<tr><td><code>:codes</code></td><td>List all type aliases</td></tr>
|
|
2154
|
+
<tr><td><code>:type <name></code></td><td>Display definition of type <code><name></code></td></tr>
|
|
2155
|
+
<tr><td><code>:codes</code></td><td>List all type aliases and canonical hashes</td></tr>
|
|
2017
2156
|
<tr><td><code>:rels</code></td><td>List all relation aliases</td></tr>
|
|
2018
|
-
<tr><td><code>:codecs</code></td><td>List all
|
|
2019
|
-
<tr><td><code>:codec load <name|file></code></td><td>Load a
|
|
2020
|
-
<tr><td><code>:codec define n t b</code></td><td>Define inline custom codec: <code>name type { parse: ..., print: ... }</code></td></tr>
|
|
2157
|
+
<tr><td><code>:codecs</code></td><td>List all registered format codecs</td></tr>
|
|
2158
|
+
<tr><td><code>:codec load <name|file></code></td><td>Load a format codec (<code>int</code>, <code>utf8</code>, <code>json</code>, <code>ieee</code>, <code>unit</code>) or file</td></tr>
|
|
2021
2159
|
<tr><td><code>:codec unload <name></code></td><td>Unload a registered codec</td></tr>
|
|
2022
|
-
<tr><td><code>:input <
|
|
2023
|
-
<tr><td><code>:
|
|
2160
|
+
<tr><td><code>:input [<filter=(...)> [codec]]</code></td><td>Read next input line using specified codec (opens dialog in browser)</td></tr>
|
|
2161
|
+
<tr><td><code>:time <expr></code></td><td>Evaluate <code><expr></code> and report execution time</td></tr>
|
|
2024
2162
|
<tr><td><code>:klib <file></code></td><td>Export current state as a <code>.klib</code> binary file</td></tr>
|
|
2025
2163
|
<tr><td><code>:reset</code></td><td>Clear state and reset to initial environment</td></tr>
|
|
2026
2164
|
<tr><td><code>:help</code></td><td>Show command reference</td></tr>
|
|
@@ -2031,10 +2169,13 @@ const htmlContent = `<!DOCTYPE html>
|
|
|
2031
2169
|
<h4>Expressions & Snippets</h4>
|
|
2032
2170
|
<p style="margin-bottom: 8px; color: var(--text-muted);">Any raw K expression entered at the prompt is compiled and evaluated on top of the current state using WebAssembly.</p>
|
|
2033
2171
|
<ul>
|
|
2034
|
-
<li><code>
|
|
2035
|
-
<li><code>
|
|
2036
|
-
<li><code>
|
|
2037
|
-
<li><code>$
|
|
2172
|
+
<li><code>{} | ok</code> — Constant relation producing variant <code>ok</code> with empty document payload <code>{}</code></li>
|
|
2173
|
+
<li><code>not = < / true | false, / false | true >;</code> — Polymorphic negation via branch projections and ordered choice</li>
|
|
2174
|
+
<li><code>{} | true not</code> — Compose constant relation <code>{} | true</code> with <code>not</code> (evaluates to <code>{} | false</code>)</li>
|
|
2175
|
+
<li><code>$ bool = < {} true, {} false >;</code> — Define a boolean union type schema</li>
|
|
2176
|
+
<li><code>not = $ bool < / true | false, {} | true > $ bool;</code> — Typed boolean negation: guarded by <code>$ bool</code>, defaulting to <code>{} | true</code></li>
|
|
2177
|
+
<li><code>swap = { . y x, . x y };</code> — Define a product field-swapping relation</li>
|
|
2178
|
+
<li><code>{ {} | ok x, {} | nil y } swap</code> — Construct a product and pass it through <code>swap</code></li>
|
|
2038
2179
|
</ul>
|
|
2039
2180
|
</div>
|
|
2040
2181
|
|
package/tests/test-repl-html.mjs
CHANGED
|
@@ -145,6 +145,7 @@ if (chromiumBin) {
|
|
|
145
145
|
"ieee.mjs",
|
|
146
146
|
"int.mjs",
|
|
147
147
|
"json.mjs",
|
|
148
|
+
"poly.k",
|
|
148
149
|
"unit.mjs",
|
|
149
150
|
"utf8.mjs"
|
|
150
151
|
].sort();
|
|
@@ -202,14 +203,16 @@ if (chromiumBin) {
|
|
|
202
203
|
})()`);
|
|
203
204
|
assert(formattedVal.includes("int: 10"), `Expected 'int: 10' in output, got: ${formattedVal}`);
|
|
204
205
|
|
|
205
|
-
// Test defining
|
|
206
|
-
await evaluateAsync(`window.kRepl.executeCommand("
|
|
206
|
+
// Test defining custom codec via Custom Codec Studio UI
|
|
207
|
+
await evaluateAsync(`window.kRepl.executeCommand("$ yes_no = < {} yes, {} no >;");`);
|
|
207
208
|
const defineCodecOut = await evaluateAsync(`(async () => {
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
209
|
+
window.kRepl.openCodecsModal();
|
|
210
|
+
const typeSelect = document.getElementById("codec-custom-type");
|
|
211
|
+
if (typeSelect) typeSelect.value = "yes_no";
|
|
212
|
+
document.getElementById("btn-register-codec").click();
|
|
213
|
+
return document.getElementById("codec-studio-status")?.textContent || "";
|
|
211
214
|
})()`);
|
|
212
|
-
assert(defineCodecOut.includes("
|
|
215
|
+
assert(defineCodecOut.includes("Successfully registered codec yn"), `Expected defined codec yn, got: ${defineCodecOut}`);
|
|
213
216
|
|
|
214
217
|
// Test modal interaction via openCodecsModal
|
|
215
218
|
const modalIsOpen = await evaluateAsync(`(() => {
|
|
@@ -273,6 +276,32 @@ if (chromiumBin) {
|
|
|
273
276
|
})()`);
|
|
274
277
|
assert(jsonResult.includes('json: {"a":"Woj","n":123}'), `Expected json: {"a":"Woj","n":123}, got: ${jsonResult}`);
|
|
275
278
|
|
|
279
|
+
// Verify Reset button is removed from navbar
|
|
280
|
+
const hasResetBtn = await evaluateAsync(`Boolean(document.getElementById("btn-reset"))`);
|
|
281
|
+
assert.strictEqual(hasResetBtn, false, "Reset button should be removed from navbar");
|
|
282
|
+
|
|
283
|
+
// Test explicit pattern expression :input ? {string a, float64 n} json
|
|
284
|
+
await evaluateAsync(`(async () => {
|
|
285
|
+
await window.kRepl.executeCommand(":input ? {string a, float64 n} json");
|
|
286
|
+
await window.kRepl.executeCommand('{"a":"Test","n":456}');
|
|
287
|
+
})()`);
|
|
288
|
+
const patternResult = await evaluateAsync(`(() => {
|
|
289
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
290
|
+
return lines[lines.length - 1];
|
|
291
|
+
})()`);
|
|
292
|
+
assert(patternResult.includes('json: {"a":"Test","n":456}'), `Expected json: {"a":"Test","n":456}, got: ${patternResult}`);
|
|
293
|
+
|
|
294
|
+
// Test standalone codec input :input json without pre-declared type
|
|
295
|
+
await evaluateAsync(`(async () => {
|
|
296
|
+
await window.kRepl.executeCommand(":input json");
|
|
297
|
+
await window.kRepl.executeCommand('{"foo":"bar"}');
|
|
298
|
+
})()`);
|
|
299
|
+
const standaloneResult = await evaluateAsync(`(() => {
|
|
300
|
+
const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
|
|
301
|
+
return lines[lines.length - 1];
|
|
302
|
+
})()`);
|
|
303
|
+
assert(standaloneResult.includes('json: {"foo":"bar"}'), `Expected json: {"foo":"bar"}, got: ${standaloneResult}`);
|
|
304
|
+
|
|
276
305
|
// Verify text selection holds in output element
|
|
277
306
|
const selectionCheck = await evaluateAsync(`(() => {
|
|
278
307
|
const output = document.getElementById("terminal-output");
|
|
@@ -428,6 +457,55 @@ if (chromiumBin) {
|
|
|
428
457
|
assert.strictEqual(variantDepthAndActionsCheck.allOpenAfterExpand, true, "Expand button should expand collapsed nodes");
|
|
429
458
|
assert.strictEqual(variantDepthAndActionsCheck.v4CollapsedAgain, true, "Collapse button should reset deeper nodes to collapsed");
|
|
430
459
|
|
|
460
|
+
// 4. Verify shortened welcome banner
|
|
461
|
+
const bannerCheck = await evaluateAsync(`(() => {
|
|
462
|
+
const banner = document.querySelector(".welcome-banner");
|
|
463
|
+
const title = banner?.querySelector(".welcome-title")?.textContent.trim();
|
|
464
|
+
const desc = banner?.querySelector(".welcome-desc");
|
|
465
|
+
const tips = banner?.querySelector(".welcome-tips");
|
|
466
|
+
return { hasBanner: !!banner, title, hasDesc: !!desc, hasTips: !!tips };
|
|
467
|
+
})()`);
|
|
468
|
+
assert.strictEqual(bannerCheck.hasBanner, true, "Welcome banner should be present");
|
|
469
|
+
assert.strictEqual(bannerCheck.title, "k interactive repl", "Welcome banner title should be 'k interactive repl'");
|
|
470
|
+
assert.strictEqual(bannerCheck.hasDesc, false, "Welcome banner description should be removed");
|
|
471
|
+
assert.strictEqual(bannerCheck.hasTips, false, "Welcome banner tips should be removed");
|
|
472
|
+
|
|
473
|
+
// 5. Test narrow viewport rendering (e.g. 600px width)
|
|
474
|
+
await send("Emulation.setDeviceMetricsOverride", { width: 600, height: 800, deviceScaleFactor: 1, mobile: false });
|
|
475
|
+
const narrowNavbarCheck = await evaluateAsync(`(() => {
|
|
476
|
+
const btnHelp = document.getElementById("btn-help");
|
|
477
|
+
if (!btnHelp) return { found: false };
|
|
478
|
+
const rect = btnHelp.getBoundingClientRect();
|
|
479
|
+
const isVisible = rect.width > 0 && rect.height > 0 && rect.left >= 0 && rect.right <= window.innerWidth;
|
|
480
|
+
btnHelp.click();
|
|
481
|
+
const helpModalOpen = document.getElementById("help-modal")?.classList.contains("open");
|
|
482
|
+
const modalText = document.getElementById("help-modal")?.textContent || "";
|
|
483
|
+
const hasRel = modalText.includes(":rel ");
|
|
484
|
+
const hasVal = modalText.includes(":val");
|
|
485
|
+
const hasTiming = modalText.includes(":timing");
|
|
486
|
+
const hasCodecDefine = modalText.includes(":codec define");
|
|
487
|
+
const hasType = modalText.includes(":type <name>");
|
|
488
|
+
return {
|
|
489
|
+
found: true,
|
|
490
|
+
isVisible,
|
|
491
|
+
rect: { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom },
|
|
492
|
+
helpModalOpen,
|
|
493
|
+
hasRel,
|
|
494
|
+
hasVal,
|
|
495
|
+
hasTiming,
|
|
496
|
+
hasCodecDefine,
|
|
497
|
+
hasType
|
|
498
|
+
};
|
|
499
|
+
})()`);
|
|
500
|
+
assert.strictEqual(narrowNavbarCheck.found, true, "#btn-help should exist");
|
|
501
|
+
assert.strictEqual(narrowNavbarCheck.isVisible, true, "#btn-help should be visible within narrow viewport");
|
|
502
|
+
assert.strictEqual(narrowNavbarCheck.helpModalOpen, true, "Clicking #btn-help in narrow viewport should open help modal");
|
|
503
|
+
assert.strictEqual(narrowNavbarCheck.hasRel, false, "Help modal should not contain :rel");
|
|
504
|
+
assert.strictEqual(narrowNavbarCheck.hasVal, false, "Help modal should not contain :val");
|
|
505
|
+
assert.strictEqual(narrowNavbarCheck.hasTiming, false, "Help modal should not contain :timing");
|
|
506
|
+
assert.strictEqual(narrowNavbarCheck.hasCodecDefine, false, "Help modal should not contain :codec define");
|
|
507
|
+
assert.strictEqual(narrowNavbarCheck.hasType, true, "Help modal should contain :type <name>");
|
|
508
|
+
|
|
431
509
|
ws.close();
|
|
432
510
|
console.log(" Browser execution verified successfully!");
|
|
433
511
|
} finally {
|