@fraczak/k 6.0.1 → 6.0.3
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/AGENTS.md +35 -0
- package/CHANGELOG.md +48 -0
- package/CLAUDE.md +64 -0
- package/DOCS/BIG_PICTURE.md +4 -8
- package/DOCS/BINARY_CODEC_STATUS_AND_TODO.md +74 -0
- package/DOCS/CONVERGENCE.md +92 -0
- package/DOCS/DIVERGENCE_DETECTION.md +24 -0
- package/DOCS/Makefile +99 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +221 -0
- package/DOCS/PATTERNS.md +114 -0
- package/DOCS/PATTERNS_AND_TYPED_VALUES.md +665 -0
- package/DOCS/REPL.md +127 -126
- package/DOCS/TEXTUAL_VALUES.md +108 -0
- package/DOCS/TYPE_DERIVATION.md +230 -0
- package/DOCS/book/06-values-in-memory.md +1085 -0
- package/DOCS/book/07-the-partial-function-abi.md +143 -0
- package/DOCS/book/08-operational-semantics-and-execution.md +201 -0
- package/DOCS/book/09-compiler-architecture.md +160 -0
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +167 -0
- package/DOCS/book/14-canonical-serialization.md +176 -0
- package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +155 -0
- package/DOCS/book/overview.txt +68 -0
- package/README.md +78 -30
- package/UU/ta.k +1 -0
- package/UU/ta.k~ +0 -0
- package/UU/tb.k +0 -0
- package/UU/tc.k +0 -0
- package/codecs/README.md +12 -0
- package/codecs/pandoc.css +71 -0
- package/codecs/runtime/test-codec.mjs +89 -0
- package/core.klib +0 -0
- package/core.klib~ +0 -0
- package/ieee.klib +0 -0
- package/kkk.k +197 -0
- package/kkk.klib +0 -0
- package/kkk.klib~ +0 -0
- package/object.mjs +162 -8
- package/objects/README.md +10 -0
- package/objects/compile-lib.mjs +26 -2
- package/objects/compile.mjs +26 -2
- package/objects/decompile.mjs +25 -2
- package/objects/extract-aliases.mjs +59 -0
- package/package.json +3 -2
- package/{codecs/k-pattern.mjs → patterns/from-k.mjs} +3 -3
- package/repl2.mjs +345 -83
- package/scripts/generate-dependency-graph.mjs +121 -0
- package/test-k-object.mjs +77 -1
- package/test-repl2.mjs +65 -8
- package/DOCS/book-combined.html +0 -3798
- package/DOCS/book-combined.md +0 -3745
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +0 -5682
- package/DOCS/book.html +0 -65
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +0 -150
package/repl2.mjs
CHANGED
|
@@ -28,10 +28,10 @@ const TYPE_DEF_RE = /^\s*\$\s*([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)\s*=/;
|
|
|
28
28
|
const REL_DEF_RE = /^\s*([a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*)\s*=/;
|
|
29
29
|
const COMMAND_NAMES = [
|
|
30
30
|
"help", "type", "code", "rel", "def", "run", "eval", "t", "d", "C",
|
|
31
|
-
"codes", "rels", "val", "reset", "
|
|
31
|
+
"codes", "rels", "val", "reset", "klib", "ko", "load",
|
|
32
32
|
"quit", "exit"
|
|
33
33
|
];
|
|
34
|
-
const PATH_COMMANDS = new Set(["
|
|
34
|
+
const PATH_COMMANDS = new Set(["klib", "ko", "load"]);
|
|
35
35
|
const initialCodes = codes.dump();
|
|
36
36
|
|
|
37
37
|
function cloneJSON(value) {
|
|
@@ -88,12 +88,49 @@ function aliasPreamble(state, omitName = null) {
|
|
|
88
88
|
return lines.filter(Boolean).join("\n");
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
function preambleLineCount(preamble) {
|
|
92
|
+
if (!preamble) return 0;
|
|
93
|
+
return preamble.split("\n").length;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function remapLineNumber(line, offset) {
|
|
97
|
+
return Math.max(1, line - offset);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function remapDiagnosticMessage(message, lineOffset) {
|
|
101
|
+
if (!lineOffset) return message;
|
|
102
|
+
|
|
103
|
+
let rewritten = message.replace(
|
|
104
|
+
/\(lines (\d+):(\d+)\.\.\.(\d+):(\d+)\)/g,
|
|
105
|
+
(_, startLine, startCol, endLine, endCol) =>
|
|
106
|
+
`(lines ${remapLineNumber(Number(startLine), lineOffset)}:${startCol}...${remapLineNumber(Number(endLine), lineOffset)}:${endCol})`
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
rewritten = rewritten.replace(
|
|
110
|
+
/Parse error on line (\d+):/g,
|
|
111
|
+
(_, line) => `Parse error on line ${remapLineNumber(Number(line), lineOffset)}:`
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return rewritten;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function remapError(error, lineOffset) {
|
|
118
|
+
if (!lineOffset || !error?.message) return error;
|
|
119
|
+
const remapped = new Error(remapDiagnosticMessage(error.message, lineOffset));
|
|
120
|
+
remapped.name = error.name;
|
|
121
|
+
if (error.stack) {
|
|
122
|
+
remapped.stack = error.stack.replace(error.message, remapped.message);
|
|
123
|
+
}
|
|
124
|
+
if ("cause" in error) remapped.cause = error.cause;
|
|
125
|
+
return remapped;
|
|
126
|
+
}
|
|
127
|
+
|
|
91
128
|
function compileWithOptionalIdentity(source, options) {
|
|
92
129
|
try {
|
|
93
130
|
return hydrateObject(compileLibrary(source, options));
|
|
94
131
|
} catch (error) {
|
|
95
132
|
if (!/got 'EOF'|Expecting/.test(error.message)) throw error;
|
|
96
|
-
return hydrateObject(compileLibrary(`${source}\n()`, options));
|
|
133
|
+
return hydrateObject(compileLibrary(`${ensureSemicolon(source)}\n()`, options));
|
|
97
134
|
}
|
|
98
135
|
}
|
|
99
136
|
|
|
@@ -102,28 +139,34 @@ function annotateWithOptionalIdentity(source, options) {
|
|
|
102
139
|
return annotate(source, options);
|
|
103
140
|
} catch (error) {
|
|
104
141
|
if (!/got 'EOF'|Expecting/.test(error.message)) throw error;
|
|
105
|
-
return annotate(`${source}\n()`, options);
|
|
142
|
+
return annotate(`${ensureSemicolon(source)}\n()`, options);
|
|
106
143
|
}
|
|
107
144
|
}
|
|
108
145
|
|
|
109
146
|
function mergeMeta(state, meta = {}) {
|
|
110
147
|
for (const [hash, entry] of Object.entries(meta)) {
|
|
111
148
|
const origins = entry?.origins || [];
|
|
112
|
-
|
|
113
|
-
state.meta[hash].origins
|
|
149
|
+
const entryType = entry?.type;
|
|
150
|
+
if (!state.meta[hash]) state.meta[hash] = { type: entryType, origins: [] };
|
|
151
|
+
if (state.meta[hash].type == null && entryType != null) state.meta[hash].type = entryType;
|
|
152
|
+
state.meta[hash].origins.push(...origins.map(({ source, name, compiledAt }) => ({
|
|
153
|
+
source,
|
|
154
|
+
name,
|
|
155
|
+
...(compiledAt == null ? {} : { compiledAt })
|
|
156
|
+
})));
|
|
114
157
|
}
|
|
115
158
|
}
|
|
116
159
|
|
|
117
|
-
function rememberOrigin(state, hash, name,
|
|
118
|
-
if (!state.meta[hash]) state.meta[hash] = { origins: [] };
|
|
160
|
+
function rememberOrigin(state, hash, name, type, source = "<repl>") {
|
|
161
|
+
if (!state.meta[hash]) state.meta[hash] = { type, origins: [] };
|
|
162
|
+
if (state.meta[hash].type == null) state.meta[hash].type = type;
|
|
119
163
|
const exists = state.meta[hash].origins.some((origin) =>
|
|
120
|
-
origin.name === name &&
|
|
164
|
+
origin.name === name && state.meta[hash].type === type && origin.source === source
|
|
121
165
|
);
|
|
122
166
|
if (!exists) {
|
|
123
167
|
state.meta[hash].origins.push({
|
|
124
168
|
source,
|
|
125
169
|
name,
|
|
126
|
-
kind,
|
|
127
170
|
compiledAt: new Date().toISOString()
|
|
128
171
|
});
|
|
129
172
|
}
|
|
@@ -133,40 +176,47 @@ function recoverAliasesFromMeta(state, lib) {
|
|
|
133
176
|
for (const [hash, entry] of Object.entries(lib.meta || {})) {
|
|
134
177
|
for (const origin of entry?.origins || []) {
|
|
135
178
|
if (!origin?.name || !NAME_RE.test(origin.name)) continue;
|
|
136
|
-
|
|
179
|
+
const type = entry?.type;
|
|
180
|
+
if (type === "code" || (!type && hash in state.codes && !(hash in state.rels))) {
|
|
137
181
|
state.typeAliases[origin.name] = hash;
|
|
138
|
-
} else if (
|
|
182
|
+
} else if (type === "rel" || hash in state.rels) {
|
|
139
183
|
state.relAliases[origin.name] = hash;
|
|
140
184
|
}
|
|
141
185
|
}
|
|
142
186
|
}
|
|
143
187
|
}
|
|
144
188
|
|
|
145
|
-
function mergeLibrary(state, lib, source = "<load>") {
|
|
189
|
+
function mergeLibrary(state, lib, source = "<load>", options = {}) {
|
|
190
|
+
const loadAliases = options.loadAliases ?? true;
|
|
146
191
|
state.codes = { ...state.codes, ...(lib.codes || {}) };
|
|
147
192
|
state.rels = { ...state.rels, ...(lib.rels || {}) };
|
|
148
|
-
state.relAliases = { ...state.relAliases, ...(lib.relAlias || {}) };
|
|
149
193
|
mergeMeta(state, lib.meta);
|
|
150
194
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
195
|
+
if (loadAliases) {
|
|
196
|
+
state.relAliases = { ...state.relAliases, ...(lib.relAlias || {}) };
|
|
197
|
+
for (const [name, hash] of Object.entries(lib.relAlias || {})) {
|
|
198
|
+
if (name !== "__main__" && NAME_RE.test(name) && hash in state.rels) {
|
|
199
|
+
state.relAliases[name] = hash;
|
|
200
|
+
rememberOrigin(state, hash, name, "rel", source);
|
|
201
|
+
}
|
|
155
202
|
}
|
|
203
|
+
recoverAliasesFromMeta(state, lib);
|
|
156
204
|
}
|
|
157
|
-
recoverAliasesFromMeta(state, lib);
|
|
158
205
|
restoreCodes(state);
|
|
159
206
|
}
|
|
160
207
|
|
|
161
208
|
function savedLibrary(state) {
|
|
162
209
|
const meta = cloneJSON(state.meta);
|
|
210
|
+
const now = new Date().toISOString();
|
|
163
211
|
for (const [name, hash] of Object.entries(state.typeAliases)) {
|
|
164
|
-
if (!meta[hash]) meta[hash] = { origins: [] };
|
|
165
|
-
meta[hash].
|
|
212
|
+
if (!meta[hash]) meta[hash] = { type: "code", origins: [] };
|
|
213
|
+
if (meta[hash].type == null) meta[hash].type = "code";
|
|
214
|
+
meta[hash].origins.push({ source: "<repl>", name, compiledAt: now });
|
|
166
215
|
}
|
|
167
216
|
for (const [name, hash] of Object.entries(state.relAliases)) {
|
|
168
|
-
if (!meta[hash]) meta[hash] = { origins: [] };
|
|
169
|
-
meta[hash].
|
|
217
|
+
if (!meta[hash]) meta[hash] = { type: "rel", origins: [] };
|
|
218
|
+
if (meta[hash].type == null) meta[hash].type = "rel";
|
|
219
|
+
meta[hash].origins.push({ source: "<repl>", name, compiledAt: now });
|
|
170
220
|
}
|
|
171
221
|
return {
|
|
172
222
|
format: "k-object",
|
|
@@ -183,15 +233,18 @@ function savedLibrary(state) {
|
|
|
183
233
|
function executableObject(state, mainExpression) {
|
|
184
234
|
const main = (mainExpression || state.lastMain || "").trim();
|
|
185
235
|
if (!main) {
|
|
186
|
-
throw new Error(":ko requires a main expression unless one has already been evaluated or defined");
|
|
236
|
+
throw new Error(":ko file expr requires a main expression unless one has already been evaluated or defined");
|
|
187
237
|
}
|
|
188
238
|
|
|
189
239
|
restoreCodes(state);
|
|
240
|
+
const preamble = aliasPreamble(state);
|
|
190
241
|
try {
|
|
191
|
-
return compileObject([
|
|
242
|
+
return compileObject([preamble, main].filter(Boolean).join("\n"), {
|
|
192
243
|
source: "<repl>",
|
|
193
244
|
libraries: [stateLibrary(state)]
|
|
194
245
|
});
|
|
246
|
+
} catch (error) {
|
|
247
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
195
248
|
} finally {
|
|
196
249
|
restoreCodes(state);
|
|
197
250
|
}
|
|
@@ -245,8 +298,8 @@ function libraryOriginsFromSource(source, fullSource, lib, options) {
|
|
|
245
298
|
for (const name of typeNames) {
|
|
246
299
|
const hash = annotated.representatives?.[name];
|
|
247
300
|
if (!hash) continue;
|
|
248
|
-
if (!meta[hash]) meta[hash] = { origins: [] };
|
|
249
|
-
meta[hash].origins.push({ source: options.source || null, name,
|
|
301
|
+
if (!meta[hash]) meta[hash] = { type: "code", origins: [] };
|
|
302
|
+
meta[hash].origins.push({ source: options.source || null, name, compiledAt: now });
|
|
250
303
|
}
|
|
251
304
|
|
|
252
305
|
const relAlias = Object.fromEntries(
|
|
@@ -256,8 +309,8 @@ function libraryOriginsFromSource(source, fullSource, lib, options) {
|
|
|
256
309
|
);
|
|
257
310
|
|
|
258
311
|
for (const [name, hash] of Object.entries(relAlias)) {
|
|
259
|
-
if (!meta[hash]) meta[hash] = { origins: [] };
|
|
260
|
-
meta[hash].origins.push({ source: options.source || null, name,
|
|
312
|
+
if (!meta[hash]) meta[hash] = { type: "rel", origins: [] };
|
|
313
|
+
meta[hash].origins.push({ source: options.source || null, name, compiledAt: now });
|
|
261
314
|
}
|
|
262
315
|
|
|
263
316
|
return { relAlias, meta };
|
|
@@ -272,6 +325,13 @@ function canonicalNames(state) {
|
|
|
272
325
|
])].filter((name) => name.startsWith("@")).sort();
|
|
273
326
|
}
|
|
274
327
|
|
|
328
|
+
function aliasNames(state) {
|
|
329
|
+
return [...new Set([
|
|
330
|
+
...Object.keys(state.typeAliases),
|
|
331
|
+
...Object.keys(state.relAliases)
|
|
332
|
+
])].sort();
|
|
333
|
+
}
|
|
334
|
+
|
|
275
335
|
function expandHome(input) {
|
|
276
336
|
if (input === "~") return os.homedir();
|
|
277
337
|
if (input.startsWith("~/")) return path.join(os.homedir(), input.slice(2));
|
|
@@ -303,6 +363,19 @@ function completeCanonical(line, state) {
|
|
|
303
363
|
return [matches, line];
|
|
304
364
|
}
|
|
305
365
|
|
|
366
|
+
function completeIdentifier(line, state) {
|
|
367
|
+
const match = line.match(/^(.*?)(\$?[A-Za-z0-9_+-][A-Za-z0-9_?!+-]*)$/);
|
|
368
|
+
if (!match) return [[], line];
|
|
369
|
+
const [, prefix, partial] = match;
|
|
370
|
+
const isTypeToken = partial.startsWith("$");
|
|
371
|
+
const barePartial = isTypeToken ? partial.slice(1) : partial;
|
|
372
|
+
const names = isTypeToken ? Object.keys(state.typeAliases).sort() : aliasNames(state);
|
|
373
|
+
const matches = names
|
|
374
|
+
.filter((name) => name.startsWith(barePartial))
|
|
375
|
+
.map((name) => `${prefix}${isTypeToken ? "$" : ""}${name}`);
|
|
376
|
+
return [matches, line];
|
|
377
|
+
}
|
|
378
|
+
|
|
306
379
|
function completePath(line, tokenStart) {
|
|
307
380
|
const token = line.slice(tokenStart);
|
|
308
381
|
const quote = token[0] === "\"" || token[0] === "'" ? token[0] : "";
|
|
@@ -341,6 +414,26 @@ function commandArgIndex(arg) {
|
|
|
341
414
|
return trimmed.split(/\s+/).length - 1;
|
|
342
415
|
}
|
|
343
416
|
|
|
417
|
+
function loadPathCompletionStart(argStart, arg) {
|
|
418
|
+
const leadingWhitespace = arg.length - arg.trimStart().length;
|
|
419
|
+
const trimmed = arg.trimStart();
|
|
420
|
+
if (trimmed === "" || !trimmed.startsWith("--no-alias")) {
|
|
421
|
+
if (commandArgIndex(arg) !== 0) return null;
|
|
422
|
+
return argStart + completionTokenStart(arg);
|
|
423
|
+
}
|
|
424
|
+
if (!"--no-alias".startsWith(trimmed) && !trimmed.startsWith("--no-alias ")) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
if (trimmed !== "--no-alias" && !trimmed.startsWith("--no-alias ")) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
if (trimmed === "--no-alias") return null;
|
|
431
|
+
const restStart = leadingWhitespace + "--no-alias".length;
|
|
432
|
+
const rest = arg.slice(restStart);
|
|
433
|
+
const restLeadingWhitespace = rest.length - rest.trimStart().length;
|
|
434
|
+
return argStart + restStart + restLeadingWhitespace + completionTokenStart(rest.trimStart());
|
|
435
|
+
}
|
|
436
|
+
|
|
344
437
|
function completeCommandArgument(line, state) {
|
|
345
438
|
const body = line.slice(1);
|
|
346
439
|
const firstSpace = body.search(/\s/);
|
|
@@ -350,8 +443,13 @@ function completeCommandArgument(line, state) {
|
|
|
350
443
|
const argStart = 1 + firstSpace + 1;
|
|
351
444
|
const arg = line.slice(argStart);
|
|
352
445
|
|
|
353
|
-
if (PATH_COMMANDS.has(command)
|
|
354
|
-
const tokenStart =
|
|
446
|
+
if (PATH_COMMANDS.has(command)) {
|
|
447
|
+
const tokenStart = command === "load"
|
|
448
|
+
? loadPathCompletionStart(argStart, arg)
|
|
449
|
+
: commandArgIndex(arg) === 0
|
|
450
|
+
? argStart + completionTokenStart(arg)
|
|
451
|
+
: null;
|
|
452
|
+
if (tokenStart == null) return [[], line];
|
|
355
453
|
return completePath(line, tokenStart);
|
|
356
454
|
}
|
|
357
455
|
|
|
@@ -359,6 +457,10 @@ function completeCommandArgument(line, state) {
|
|
|
359
457
|
return completeCanonical(line, state);
|
|
360
458
|
}
|
|
361
459
|
|
|
460
|
+
if (/\$?[A-Za-z0-9_+-][A-Za-z0-9_?!+-]*$/.test(line)) {
|
|
461
|
+
return completeIdentifier(line, state);
|
|
462
|
+
}
|
|
463
|
+
|
|
362
464
|
return [[], line];
|
|
363
465
|
}
|
|
364
466
|
|
|
@@ -369,6 +471,9 @@ function completeInput(line, state) {
|
|
|
369
471
|
if (/@[A-Za-z0-9_?!+-]*$/.test(line)) {
|
|
370
472
|
return completeCanonical(line, state);
|
|
371
473
|
}
|
|
474
|
+
if (/\$?[A-Za-z0-9_+-][A-Za-z0-9_?!+-]*$/.test(line)) {
|
|
475
|
+
return completeIdentifier(line, state);
|
|
476
|
+
}
|
|
372
477
|
return [[], line];
|
|
373
478
|
}
|
|
374
479
|
|
|
@@ -389,6 +494,90 @@ function ensureSemicolon(source) {
|
|
|
389
494
|
return source.trim().endsWith(";") ? source : `${source};`;
|
|
390
495
|
}
|
|
391
496
|
|
|
497
|
+
function isEofParseError(error) {
|
|
498
|
+
return /got 'EOF'|Unexpected end of input/.test(error.message || "");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function parseLoadArgs(arg, usagePrefix = ":") {
|
|
502
|
+
const trimmed = arg.trim();
|
|
503
|
+
if (!trimmed) throw new Error(`${usagePrefix}load requires a file path`);
|
|
504
|
+
if (trimmed === "--no-alias") {
|
|
505
|
+
throw new Error(`${usagePrefix}load --no-alias requires a file path`);
|
|
506
|
+
}
|
|
507
|
+
if (trimmed.startsWith("--no-alias ")) {
|
|
508
|
+
const path = trimmed.slice("--no-alias".length).trim();
|
|
509
|
+
if (!path) throw new Error(`${usagePrefix}load --no-alias requires a file path`);
|
|
510
|
+
return { path, loadAliases: false };
|
|
511
|
+
}
|
|
512
|
+
return { path: arg.trim(), loadAliases: true };
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function lineForContinuation(line) {
|
|
516
|
+
return line.replace(/\\\s*$/, "");
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function lineHasExplicitContinuation(line) {
|
|
520
|
+
return /\\\s*$/.test(line);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function lineTerminatesSnippet(line) {
|
|
524
|
+
return /;\s*$/.test(line);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function explicitSnippetTerminated(source) {
|
|
528
|
+
const lines = source.split("\n");
|
|
529
|
+
const lastLine = lines[lines.length - 1] || "";
|
|
530
|
+
return lineTerminatesSnippet(lastLine);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function analyzeRawSnippet(source) {
|
|
534
|
+
try {
|
|
535
|
+
const parsed = parse(source);
|
|
536
|
+
return { kind: "withMain", parsed };
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (!isEofParseError(error)) throw error;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return { kind: "incomplete", parsed: null };
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function analyzeAcceptedSnippet(source, explicitTerminated = explicitSnippetTerminated(source)) {
|
|
545
|
+
try {
|
|
546
|
+
const parsed = parse(source);
|
|
547
|
+
return { kind: "withMain", parsed };
|
|
548
|
+
} catch (error) {
|
|
549
|
+
if (!explicitTerminated) {
|
|
550
|
+
if (isEofParseError(error)) return { kind: "incomplete", parsed: null };
|
|
551
|
+
throw error;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const parsed = parse(`${source}\n()`);
|
|
556
|
+
return { kind: "definitionsOnly", parsed };
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function compileSnippetArtifacts(source, state, sourceName = "<repl>") {
|
|
560
|
+
restoreCodes(state);
|
|
561
|
+
const preamble = aliasPreamble(state);
|
|
562
|
+
const fullSource = [preamble, source].filter(Boolean).join("\n");
|
|
563
|
+
const options = {
|
|
564
|
+
source: sourceName,
|
|
565
|
+
libraries: [stateLibrary(state)]
|
|
566
|
+
};
|
|
567
|
+
try {
|
|
568
|
+
const lib = compileWithOptionalIdentity(fullSource, options);
|
|
569
|
+
const { relAlias, meta } = libraryOriginsFromSource(source, fullSource, lib, options);
|
|
570
|
+
const annotated = annotateWithOptionalIdentity(fullSource, options);
|
|
571
|
+
return {
|
|
572
|
+
annotated,
|
|
573
|
+
lib: { ...lib, relAlias, meta },
|
|
574
|
+
lineOffset: preambleLineCount(preamble)
|
|
575
|
+
};
|
|
576
|
+
} catch (error) {
|
|
577
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
392
581
|
async function defineType(input, state) {
|
|
393
582
|
const typeMatch = input.match(TYPE_DEF_RE);
|
|
394
583
|
if (!typeMatch) {
|
|
@@ -397,8 +586,14 @@ async function defineType(input, state) {
|
|
|
397
586
|
|
|
398
587
|
const name = typeMatch[1];
|
|
399
588
|
restoreCodes(state);
|
|
400
|
-
const
|
|
401
|
-
const
|
|
589
|
+
const preamble = aliasPreamble(state, name);
|
|
590
|
+
const source = [preamble, ensureSemicolon(input), "()"].filter(Boolean).join("\n");
|
|
591
|
+
let annotated;
|
|
592
|
+
try {
|
|
593
|
+
annotated = annotate(source, { libraries: [stateLibrary(state)] });
|
|
594
|
+
} catch (error) {
|
|
595
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
596
|
+
}
|
|
402
597
|
const hash = annotated.representatives[name];
|
|
403
598
|
if (!hash) throw new Error(`Type definition did not produce an alias for '${name}'`);
|
|
404
599
|
state.codes = codes.dump();
|
|
@@ -415,8 +610,14 @@ async function defineRelation(input, state) {
|
|
|
415
610
|
|
|
416
611
|
const name = relMatch[1];
|
|
417
612
|
restoreCodes(state);
|
|
418
|
-
const
|
|
419
|
-
const
|
|
613
|
+
const preamble = aliasPreamble(state, name);
|
|
614
|
+
const source = [preamble, ensureSemicolon(input), name].filter(Boolean).join("\n");
|
|
615
|
+
let lib;
|
|
616
|
+
try {
|
|
617
|
+
lib = hydrateObject(compileLibrary(source, { source: "<repl>", libraries: [stateLibrary(state)] }));
|
|
618
|
+
} catch (error) {
|
|
619
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
620
|
+
}
|
|
420
621
|
const hash = lib.relAlias?.[name];
|
|
421
622
|
if (!hash || !lib.rels?.[hash]) throw new Error(`Relation definition did not produce an alias for '${name}'`);
|
|
422
623
|
state.codes = codes.dump();
|
|
@@ -433,16 +634,52 @@ async function runExpression(input, state) {
|
|
|
433
634
|
if (!expression) throw new Error(":run requires an expression");
|
|
434
635
|
|
|
435
636
|
restoreCodes(state);
|
|
436
|
-
const
|
|
437
|
-
const
|
|
637
|
+
const preamble = aliasPreamble(state);
|
|
638
|
+
const source = [preamble, expression].filter(Boolean).join("\n");
|
|
639
|
+
let annotated;
|
|
640
|
+
try {
|
|
641
|
+
annotated = annotate(source, { libraries: [stateLibrary(state)] });
|
|
642
|
+
} catch (error) {
|
|
643
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
644
|
+
}
|
|
438
645
|
const mainRel = annotated.rels.__main__;
|
|
439
646
|
run.defs = annotated;
|
|
440
|
-
|
|
647
|
+
try {
|
|
648
|
+
state.value = run(codes.find, mainRel.def, state.value, mainRel.typePatternGraph);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
651
|
+
}
|
|
441
652
|
state.lastMain = expression;
|
|
442
653
|
restoreCodes(state);
|
|
443
654
|
return [printValue(state.value)];
|
|
444
655
|
}
|
|
445
656
|
|
|
657
|
+
async function runSnippet(input, state, options = {}) {
|
|
658
|
+
const snippet = input.trim();
|
|
659
|
+
if (!snippet) return [];
|
|
660
|
+
|
|
661
|
+
const explicitTerminated = options.explicitTerminated ?? explicitSnippetTerminated(input);
|
|
662
|
+
const analysis = analyzeAcceptedSnippet(snippet, explicitTerminated);
|
|
663
|
+
const { annotated, lib, lineOffset } = compileSnippetArtifacts(snippet, state);
|
|
664
|
+
mergeLibrary(state, lib, "<repl>");
|
|
665
|
+
|
|
666
|
+
if (analysis.kind === "definitionsOnly") {
|
|
667
|
+
restoreCodes(state);
|
|
668
|
+
return [];
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const mainRel = annotated.rels.__main__;
|
|
672
|
+
run.defs = annotated;
|
|
673
|
+
try {
|
|
674
|
+
state.value = run(codes.find, mainRel.def, state.value, mainRel.typePatternGraph);
|
|
675
|
+
} catch (error) {
|
|
676
|
+
throw remapError(error, lineOffset);
|
|
677
|
+
}
|
|
678
|
+
state.lastMain = snippet;
|
|
679
|
+
restoreCodes(state);
|
|
680
|
+
return [printValue(state.value)];
|
|
681
|
+
}
|
|
682
|
+
|
|
446
683
|
async function evaluateInput(input, state) {
|
|
447
684
|
const line = input.trim();
|
|
448
685
|
if (line === "" || line.startsWith("#") || line.startsWith("//") || line.startsWith("--")) return [];
|
|
@@ -451,9 +688,7 @@ async function evaluateInput(input, state) {
|
|
|
451
688
|
return evaluateCommand(line, state);
|
|
452
689
|
}
|
|
453
690
|
|
|
454
|
-
|
|
455
|
-
if (input.match(REL_DEF_RE)) return defineRelation(input, state);
|
|
456
|
-
return runExpression(input, state);
|
|
691
|
+
return runSnippet(input, state);
|
|
457
692
|
}
|
|
458
693
|
|
|
459
694
|
function parseCommand(line) {
|
|
@@ -503,57 +738,45 @@ async function evaluateCommand(line, state) {
|
|
|
503
738
|
Object.assign(state, fresh);
|
|
504
739
|
return ["reset"];
|
|
505
740
|
}
|
|
506
|
-
case "save": {
|
|
507
|
-
if (!arg) throw new Error(`${usagePrefix}save requires a file path`);
|
|
508
|
-
fs.writeFileSync(arg, encodeLibrary(savedLibrary(state)));
|
|
509
|
-
return [`saved ${arg}`];
|
|
510
|
-
}
|
|
511
741
|
case "klib": {
|
|
512
742
|
if (!arg) throw new Error(`${usagePrefix}klib requires a file path`);
|
|
513
743
|
fs.writeFileSync(arg, encodeLibrary(savedLibrary(state)));
|
|
514
744
|
return [`saved ${arg}`];
|
|
515
745
|
}
|
|
516
746
|
case "ko": {
|
|
517
|
-
if (!arg) throw new Error(`${usagePrefix}ko requires a file path`);
|
|
518
|
-
const [path, ...mainParts] = arg.split(/\s+/);
|
|
519
|
-
const object = executableObject(state, mainParts.join(" "));
|
|
520
|
-
fs.writeFileSync(path, encodeObject(object));
|
|
521
|
-
const main = (mainParts.join(" ") || state.lastMain).trim();
|
|
522
|
-
return [`saved ${path} (${main})`];
|
|
523
|
-
}
|
|
524
|
-
case "export": {
|
|
525
|
-
if (!arg) throw new Error(`${usagePrefix}export requires a file path`);
|
|
747
|
+
if (!arg) throw new Error(`${usagePrefix}ko requires a file path and main expression`);
|
|
526
748
|
const [path, ...mainParts] = arg.split(/\s+/);
|
|
527
|
-
if (
|
|
528
|
-
|
|
529
|
-
return [`saved ${path}`];
|
|
530
|
-
}
|
|
531
|
-
if (!path.endsWith(".ko")) {
|
|
532
|
-
throw new Error(":export writes .klib or .ko files; use :klib or :ko for explicit export");
|
|
749
|
+
if (mainParts.length === 0) {
|
|
750
|
+
throw new Error(":ko file expr requires a main expression");
|
|
533
751
|
}
|
|
534
752
|
const object = executableObject(state, mainParts.join(" "));
|
|
535
753
|
fs.writeFileSync(path, encodeObject(object));
|
|
536
|
-
const main =
|
|
754
|
+
const main = mainParts.join(" ").trim();
|
|
537
755
|
return [`saved ${path} (${main})`];
|
|
538
756
|
}
|
|
539
757
|
case "load": {
|
|
540
|
-
|
|
541
|
-
if (
|
|
542
|
-
const lib = loadLibrary(decodeLibrary(fs.readFileSync(
|
|
543
|
-
mergeLibrary(state, lib,
|
|
758
|
+
const { path: loadPath, loadAliases } = parseLoadArgs(arg, usagePrefix);
|
|
759
|
+
if (loadPath.endsWith(".klib")) {
|
|
760
|
+
const lib = loadLibrary(decodeLibrary(fs.readFileSync(loadPath)));
|
|
761
|
+
mergeLibrary(state, lib, loadPath, { loadAliases });
|
|
544
762
|
} else {
|
|
545
763
|
restoreCodes(state);
|
|
546
|
-
const source = fs.readFileSync(
|
|
547
|
-
const
|
|
764
|
+
const source = fs.readFileSync(loadPath, "utf8");
|
|
765
|
+
const preamble = aliasPreamble(state);
|
|
766
|
+
const fullSource = [preamble, source].filter(Boolean).join("\n");
|
|
548
767
|
const options = {
|
|
549
|
-
source:
|
|
768
|
+
source: loadPath,
|
|
550
769
|
libraries: [stateLibrary(state)]
|
|
551
770
|
};
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
771
|
+
try {
|
|
772
|
+
const lib = compileWithOptionalIdentity(fullSource, options);
|
|
773
|
+
const { relAlias, meta } = libraryOriginsFromSource(source, fullSource, lib, options);
|
|
774
|
+
mergeLibrary(state, { ...lib, relAlias, meta }, loadPath, { loadAliases });
|
|
775
|
+
} catch (error) {
|
|
776
|
+
throw remapError(error, preambleLineCount(preamble));
|
|
777
|
+
}
|
|
555
778
|
}
|
|
556
|
-
return [`loaded ${
|
|
779
|
+
return [`loaded ${loadPath}`];
|
|
557
780
|
}
|
|
558
781
|
case "t": {
|
|
559
782
|
if (!arg) throw new Error(`${usagePrefix}t requires a relation name`);
|
|
@@ -588,15 +811,17 @@ function helpText() {
|
|
|
588
811
|
":type name show type definition",
|
|
589
812
|
":codes list type aliases",
|
|
590
813
|
":rels list relation aliases",
|
|
591
|
-
":load file
|
|
592
|
-
"
|
|
593
|
-
":
|
|
594
|
-
":
|
|
814
|
+
":load [--no-alias] file",
|
|
815
|
+
" load .k source or .klib",
|
|
816
|
+
":klib file export state as a library",
|
|
817
|
+
":ko file expr export executable .ko using expr as main",
|
|
595
818
|
":val print current value",
|
|
596
819
|
":reset clear state",
|
|
597
820
|
":help show this help",
|
|
598
821
|
"",
|
|
599
|
-
"Raw k
|
|
822
|
+
"Raw k input is compiled as a snippet on top of the current state.",
|
|
823
|
+
"A line ending with ';' plus only spaces closes the snippet.",
|
|
824
|
+
"Use '\\' to force continuation."
|
|
600
825
|
].join("\n");
|
|
601
826
|
}
|
|
602
827
|
|
|
@@ -615,19 +840,50 @@ function startRepl() {
|
|
|
615
840
|
console.log("k interpreter (.klib-backed). Type :help for commands.");
|
|
616
841
|
rl.prompt();
|
|
617
842
|
async function handleLine(line) {
|
|
618
|
-
if (line
|
|
619
|
-
buffer.push(line
|
|
843
|
+
if (lineHasExplicitContinuation(line)) {
|
|
844
|
+
buffer.push(lineForContinuation(line));
|
|
620
845
|
rl.setPrompt(" ");
|
|
621
846
|
if (!closed) rl.prompt();
|
|
622
847
|
return;
|
|
623
848
|
}
|
|
624
849
|
|
|
625
|
-
|
|
850
|
+
if (buffer.length === 0 && line.trim().startsWith(":")) {
|
|
851
|
+
rl.setPrompt("> ");
|
|
852
|
+
try {
|
|
853
|
+
for (const output of await evaluateInput(line, state)) {
|
|
854
|
+
console.log(output);
|
|
855
|
+
}
|
|
856
|
+
} catch (error) {
|
|
857
|
+
console.error(error.message || String(error));
|
|
858
|
+
}
|
|
859
|
+
if (!closed) rl.prompt();
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
buffer.push(line);
|
|
864
|
+
const input = buffer.join("\n");
|
|
865
|
+
const explicitTerminated = lineTerminatesSnippet(line);
|
|
866
|
+
if (!explicitTerminated) {
|
|
867
|
+
try {
|
|
868
|
+
const analysis = analyzeRawSnippet(input);
|
|
869
|
+
if (analysis.kind !== "withMain") {
|
|
870
|
+
rl.setPrompt(" ");
|
|
871
|
+
if (!closed) rl.prompt();
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
} catch (error) {
|
|
875
|
+
buffer.length = 0;
|
|
876
|
+
rl.setPrompt("> ");
|
|
877
|
+
console.error(error.message || String(error));
|
|
878
|
+
if (!closed) rl.prompt();
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
626
883
|
buffer.length = 0;
|
|
627
884
|
rl.setPrompt("> ");
|
|
628
|
-
|
|
629
885
|
try {
|
|
630
|
-
for (const output of await
|
|
886
|
+
for (const output of await runSnippet(input, state, { explicitTerminated })) {
|
|
631
887
|
console.log(output);
|
|
632
888
|
}
|
|
633
889
|
} catch (error) {
|
|
@@ -649,13 +905,19 @@ if (isMainEntrypoint()) {
|
|
|
649
905
|
}
|
|
650
906
|
|
|
651
907
|
export {
|
|
908
|
+
aliasNames,
|
|
909
|
+
analyzeAcceptedSnippet,
|
|
910
|
+
analyzeRawSnippet,
|
|
652
911
|
canonicalNames,
|
|
653
912
|
completeInput,
|
|
654
913
|
createCompleter,
|
|
655
914
|
createState,
|
|
656
915
|
evaluateInput,
|
|
916
|
+
explicitSnippetTerminated,
|
|
657
917
|
helpText,
|
|
658
918
|
isMainEntrypoint,
|
|
919
|
+
lineHasExplicitContinuation,
|
|
920
|
+
lineTerminatesSnippet,
|
|
659
921
|
printValue,
|
|
660
922
|
propertyListToFilter,
|
|
661
923
|
valueToK
|