@fraczak/k 6.0.0 → 6.0.1
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/.codex +0 -0
- package/DOCS/a.html +398 -0
- package/DOCS/book-combined.html +3798 -0
- package/DOCS/book-combined.md +3745 -0
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +5682 -0
- package/DOCS/book.html +65 -0
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +150 -0
- package/NAT.k~ +452 -0
- package/codecs/BINARY_FORMAT.html +471 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
- package/ieee.klib +0 -0
- package/k-vscode/package-lock.json +4176 -0
- package/package.json +10 -13
- package/repl2.mjs +11 -1
- package/test-repl2.mjs +12 -1
- package/type_registry/registry.mjs~ +105 -0
- package/type_registry/typeParser.jison~ +132 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"scripts": {
|
|
@@ -10,22 +10,19 @@
|
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "
|
|
14
|
-
},
|
|
15
|
-
"publishConfig": {
|
|
16
|
-
"access": "public"
|
|
13
|
+
"url": "https://github.com/fraczak/k.git"
|
|
17
14
|
},
|
|
18
15
|
"author": "Wojciech Fraczak",
|
|
19
16
|
"license": "ISC",
|
|
20
17
|
"bin": {
|
|
21
|
-
"k": "k.mjs",
|
|
22
|
-
"k-compile-object": "objects/compile.mjs",
|
|
23
|
-
"k-decompile-object": "objects/decompile.mjs",
|
|
24
|
-
"k-parse": "codecs/k-parse.mjs",
|
|
25
|
-
"k-pattern": "codecs/k-pattern.mjs",
|
|
26
|
-
"k-print": "codecs/k-print.mjs",
|
|
27
|
-
"k-repl": "repl2.mjs",
|
|
28
|
-
"k-repl2": "repl2.mjs"
|
|
18
|
+
"k": "./k.mjs",
|
|
19
|
+
"k-compile-object": "./objects/compile.mjs",
|
|
20
|
+
"k-decompile-object": "./objects/decompile.mjs",
|
|
21
|
+
"k-parse": "./codecs/k-parse.mjs",
|
|
22
|
+
"k-pattern": "./codecs/k-pattern.mjs",
|
|
23
|
+
"k-print": "./codecs/k-print.mjs",
|
|
24
|
+
"k-repl": "./repl2.mjs",
|
|
25
|
+
"k-repl2": "./repl2.mjs"
|
|
29
26
|
},
|
|
30
27
|
"devDependencies": {
|
|
31
28
|
"jiwson": "^0.1.2"
|
package/repl2.mjs
CHANGED
|
@@ -376,6 +376,15 @@ function createCompleter(state) {
|
|
|
376
376
|
return (line) => completeInput(line, state);
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
+
function isMainEntrypoint(entryArg = argv[1]) {
|
|
380
|
+
if (!entryArg) return false;
|
|
381
|
+
try {
|
|
382
|
+
return fs.realpathSync(entryArg) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
383
|
+
} catch {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
379
388
|
function ensureSemicolon(source) {
|
|
380
389
|
return source.trim().endsWith(";") ? source : `${source};`;
|
|
381
390
|
}
|
|
@@ -635,7 +644,7 @@ function startRepl() {
|
|
|
635
644
|
});
|
|
636
645
|
}
|
|
637
646
|
|
|
638
|
-
if (
|
|
647
|
+
if (isMainEntrypoint()) {
|
|
639
648
|
startRepl();
|
|
640
649
|
}
|
|
641
650
|
|
|
@@ -646,6 +655,7 @@ export {
|
|
|
646
655
|
createState,
|
|
647
656
|
evaluateInput,
|
|
648
657
|
helpText,
|
|
658
|
+
isMainEntrypoint,
|
|
649
659
|
printValue,
|
|
650
660
|
propertyListToFilter,
|
|
651
661
|
valueToK
|
package/test-repl2.mjs
CHANGED
|
@@ -2,8 +2,9 @@ import assert from "node:assert";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
6
|
-
import { completeInput, createState, evaluateInput } from "./repl2.mjs";
|
|
7
|
+
import { completeInput, createState, evaluateInput, isMainEntrypoint } from "./repl2.mjs";
|
|
7
8
|
import { decodeObject, objectToFunction } from "./object.mjs";
|
|
8
9
|
import { Product } from "./Value.mjs";
|
|
9
10
|
|
|
@@ -44,6 +45,7 @@ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl2-"));
|
|
|
44
45
|
const libPath = path.join(tmpDir, "session.klib");
|
|
45
46
|
const koPath = path.join(tmpDir, "succ.ko");
|
|
46
47
|
const sourcePath = path.join(tmpDir, "session.k");
|
|
48
|
+
const symlinkPath = path.resolve(".test-k-repl2-link");
|
|
47
49
|
output = await evaluateInput(`:save ${libPath}`, state);
|
|
48
50
|
assert.equal(output[0], `saved ${libPath}`);
|
|
49
51
|
|
|
@@ -65,6 +67,14 @@ assert.match(output[0], /^nat = @/m);
|
|
|
65
67
|
output = await evaluateInput(":rels", loadedSource);
|
|
66
68
|
assert.match(output[0], /^succ = @/m);
|
|
67
69
|
|
|
70
|
+
const replPath = fileURLToPath(new URL("./repl2.mjs", import.meta.url));
|
|
71
|
+
try {
|
|
72
|
+
fs.rmSync(symlinkPath, { force: true });
|
|
73
|
+
} catch {}
|
|
74
|
+
fs.symlinkSync(replPath, symlinkPath);
|
|
75
|
+
fs.chmodSync(replPath, 0o755);
|
|
76
|
+
assert.equal(isMainEntrypoint(symlinkPath), true);
|
|
77
|
+
|
|
68
78
|
const reloaded = createState();
|
|
69
79
|
output = await evaluateInput(`:load ${libPath}`, reloaded);
|
|
70
80
|
assert.equal(output[0], `loaded ${libPath}`);
|
|
@@ -92,4 +102,5 @@ await assert.rejects(
|
|
|
92
102
|
);
|
|
93
103
|
|
|
94
104
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
105
|
+
fs.rmSync(symlinkPath, { force: true });
|
|
95
106
|
console.log("OK");
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import codes from '../codes.mjs';
|
|
6
|
+
import { parse } from './typeParser.mjs';
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const REGISTRY_FILE = path.join(__dirname, 'registry.json');
|
|
10
|
+
const PORT = 3001;
|
|
11
|
+
|
|
12
|
+
// Load existing registry if it exists
|
|
13
|
+
if (fs.existsSync(REGISTRY_FILE)) {
|
|
14
|
+
console.log('Loading existing registry...');
|
|
15
|
+
try {
|
|
16
|
+
const data = fs.readFileSync(REGISTRY_FILE, 'utf8');
|
|
17
|
+
const json = JSON.parse(data);
|
|
18
|
+
codes.load(json);
|
|
19
|
+
console.log(`Loaded ${Object.keys(json).length} types.`);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error('Error loading registry:', err);
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
console.log('No existing registry found. Starting fresh.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const server = http.createServer((req, res) => {
|
|
28
|
+
// Helper to send JSON response
|
|
29
|
+
const sendJSON = (data, status = 200) => {
|
|
30
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
31
|
+
res.end(JSON.stringify(data));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Helper to send Error
|
|
35
|
+
const sendError = (message, status = 400) => {
|
|
36
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
37
|
+
res.end(JSON.stringify({ error: message }));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Route: POST
|
|
41
|
+
if (req.method === 'POST') {
|
|
42
|
+
let body = '';
|
|
43
|
+
req.on('data', chunk => {
|
|
44
|
+
body += chunk.toString();
|
|
45
|
+
});
|
|
46
|
+
req.on('end', () => {
|
|
47
|
+
try {
|
|
48
|
+
// 1. Parse the input string using the Jison parser
|
|
49
|
+
// The parser expects a string of type definitions
|
|
50
|
+
console.log("Received body:", body);
|
|
51
|
+
const definitions = parse(body);
|
|
52
|
+
console.log("Parsed definitions:", JSON.stringify(definitions, null, 2));
|
|
53
|
+
|
|
54
|
+
// 2. Register the parsed codes
|
|
55
|
+
// codes.register returns the representatives map { temp_name: canonical_hash }
|
|
56
|
+
const representatives = codes.register(definitions);
|
|
57
|
+
|
|
58
|
+
// Filter out generated names (_gen_...) and canonical names (@...)
|
|
59
|
+
const filteredRepresentatives = Object.fromEntries(
|
|
60
|
+
Object.entries(representatives).filter(([key, value]) =>
|
|
61
|
+
!key.startsWith('_gen_') && !key.startsWith('@')
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// 3. Save to disk
|
|
66
|
+
const dump = codes.dump();
|
|
67
|
+
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(dump, null, 2));
|
|
68
|
+
|
|
69
|
+
// 4. Return the mapping
|
|
70
|
+
sendJSON(filteredRepresentatives);
|
|
71
|
+
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.error(err);
|
|
74
|
+
sendError(`Registration failed: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// Route: GET
|
|
79
|
+
else if (req.method === 'GET') {
|
|
80
|
+
const typeId = req.url.split('/').pop();
|
|
81
|
+
console.log("Requested type ID:", typeId);
|
|
82
|
+
if (!typeId) {
|
|
83
|
+
sendError('Missing type ID');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Decode URI component in case @ is encoded
|
|
87
|
+
const decodedId = decodeURIComponent(typeId);
|
|
88
|
+
console.log("Decoded type ID:", decodedId);
|
|
89
|
+
|
|
90
|
+
const typeDef = codes.find(decodedId);
|
|
91
|
+
if (typeDef.code === 'undefined') {
|
|
92
|
+
sendError('Type not found', 404);
|
|
93
|
+
} else {
|
|
94
|
+
sendJSON(typeDef);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 404 Not Found
|
|
98
|
+
else {
|
|
99
|
+
sendError('Not Found', 404);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
server.listen(PORT, () => {
|
|
104
|
+
console.log(`Type Registry running at http://localhost:${PORT}/`);
|
|
105
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
%{
|
|
2
|
+
|
|
3
|
+
let defs = {};
|
|
4
|
+
let genCount = 0;
|
|
5
|
+
let genNameId;
|
|
6
|
+
|
|
7
|
+
function genName() {
|
|
8
|
+
return `_gen_${genCount++}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function fromEscString(escString) {
|
|
12
|
+
const isSingleQuoted = escString.startsWith("'");
|
|
13
|
+
let str = escString.substring(1, escString.length - 1);
|
|
14
|
+
if (isSingleQuoted) {
|
|
15
|
+
str = str.replace(/\\'/g, "'");
|
|
16
|
+
} else {
|
|
17
|
+
str = str.replace(/\\"/g, '"');
|
|
18
|
+
}
|
|
19
|
+
str = str.replace(/\\\\/g, '\\');
|
|
20
|
+
str = str.replace(/\\\//g, '/');
|
|
21
|
+
str = str.replace(/\\b/g, '\b');
|
|
22
|
+
str = str.replace(/\\f/g, '\f');
|
|
23
|
+
str = str.replace(/\\n/g, '\n');
|
|
24
|
+
str = str.replace(/\\r/g, '\r');
|
|
25
|
+
str = str.replace(/\\t/g, '\t');
|
|
26
|
+
str = str.replace(/\\u([\dA-F]{4})/gi, (match, p1) => String.fromCharCode(parseInt(p1, 16)));
|
|
27
|
+
|
|
28
|
+
return str;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
%}
|
|
32
|
+
|
|
33
|
+
%lex
|
|
34
|
+
|
|
35
|
+
%%
|
|
36
|
+
\s+ /* blanks */
|
|
37
|
+
"<" return 'LA';
|
|
38
|
+
">" return 'RA';
|
|
39
|
+
"{" return 'LC';
|
|
40
|
+
"}" return 'RC';
|
|
41
|
+
"=" return 'EQ';
|
|
42
|
+
"," return 'COMMA';
|
|
43
|
+
";" return 'SC';
|
|
44
|
+
"@" return 'AT';
|
|
45
|
+
"$" return 'DOLLAR';
|
|
46
|
+
\"([^"\\]|\\(.|\n))*\"|\'([^'\\]|\\(.|\n))*\' return 'STRING';
|
|
47
|
+
[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]* return 'NAME';
|
|
48
|
+
<<EOF>> return 'EOF';
|
|
49
|
+
. return 'INVALID';
|
|
50
|
+
|
|
51
|
+
/lex
|
|
52
|
+
|
|
53
|
+
%token NAME STRING
|
|
54
|
+
%token LA RA LC RC EQ COMMA SC AT DOLLAR
|
|
55
|
+
%token EOF
|
|
56
|
+
|
|
57
|
+
%start expressions
|
|
58
|
+
|
|
59
|
+
%%
|
|
60
|
+
|
|
61
|
+
expressions
|
|
62
|
+
: initialize_defs definitions EOF
|
|
63
|
+
{ return defs; }
|
|
64
|
+
;
|
|
65
|
+
|
|
66
|
+
initialize_defs
|
|
67
|
+
: /* empty */
|
|
68
|
+
{ defs = {}; genCount = 0; }
|
|
69
|
+
;
|
|
70
|
+
|
|
71
|
+
definitions
|
|
72
|
+
: definition
|
|
73
|
+
| definitions definition
|
|
74
|
+
;
|
|
75
|
+
|
|
76
|
+
definition
|
|
77
|
+
: DOLLAR name EQ LA field_list RA SC
|
|
78
|
+
{ defs[$2] = { code: 'union', union: $5 }; }
|
|
79
|
+
| DOLLAR name EQ LC field_list RC SC
|
|
80
|
+
{ defs[$2] = { code: 'product', product: $5 }; }
|
|
81
|
+
;
|
|
82
|
+
|
|
83
|
+
type_expr
|
|
84
|
+
: LA field_list RA
|
|
85
|
+
{
|
|
86
|
+
genNameId = genName();
|
|
87
|
+
defs[genNameId] = { code: 'union', union: $2 };
|
|
88
|
+
$$ = genNameId;
|
|
89
|
+
}
|
|
90
|
+
| LC field_list RC
|
|
91
|
+
{
|
|
92
|
+
genNameId = genName();
|
|
93
|
+
defs[genNameId] = { code: 'product', product: $2 };
|
|
94
|
+
$$ = genNameId;
|
|
95
|
+
}
|
|
96
|
+
| ref
|
|
97
|
+
{ $$ = $1; }
|
|
98
|
+
;
|
|
99
|
+
|
|
100
|
+
ref
|
|
101
|
+
: name
|
|
102
|
+
{ $$ = $1; }
|
|
103
|
+
| AT name
|
|
104
|
+
{ $$ = '@' + $2; }
|
|
105
|
+
;
|
|
106
|
+
|
|
107
|
+
field_list
|
|
108
|
+
: /* empty */
|
|
109
|
+
{ $$ = {}; }
|
|
110
|
+
| field
|
|
111
|
+
{ $$ = {}; $$[$1.label] = $1.type; }
|
|
112
|
+
| field_list COMMA field
|
|
113
|
+
{ $$ = $1; $$[$3.label] = $3.type; }
|
|
114
|
+
;
|
|
115
|
+
|
|
116
|
+
field
|
|
117
|
+
: type_expr label
|
|
118
|
+
{ $$ = { type: $1, label: $2 }; }
|
|
119
|
+
;
|
|
120
|
+
|
|
121
|
+
label
|
|
122
|
+
: name
|
|
123
|
+
{ $$ = $1; }
|
|
124
|
+
| str
|
|
125
|
+
{ $$ = $1; }
|
|
126
|
+
;
|
|
127
|
+
|
|
128
|
+
name: NAME { $$ = yytext; };
|
|
129
|
+
str: STRING { $$ = fromEscString(yytext); };
|
|
130
|
+
|
|
131
|
+
%%
|
|
132
|
+
|