@fraczak/k 1.3.4 → 2.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/pretty.mjs ADDED
@@ -0,0 +1,95 @@
1
+ function compareAs(fn) {
2
+ return function (a, b) {
3
+ [a, b] = [a, b].map(fn);
4
+ if (a < b) return -1;
5
+ if (a > b) return 1;
6
+ return 0;
7
+ };
8
+ }
9
+
10
+ const nameRE = /^[a-zA-Z0-9_][a-zA-Z0-9_?!]*$/;
11
+
12
+ function prettyCode_labels (codes, representatives, label_ref_map) {
13
+ return Object.keys(label_ref_map)
14
+ .sort( compareAs((x) => x) )
15
+ .map( (label) => {
16
+ const plabel = nameRE.test(label) ? label : `'${label}'`;
17
+ return `${prettyCode(codes, representatives, {
18
+ code: "ref",
19
+ ref: label_ref_map[label],
20
+ })} ${plabel}`;
21
+ })
22
+ .join(", ");
23
+ };
24
+
25
+ function prettyCode (codes, representatives, codeExp) {
26
+ switch (codeExp.code) {
27
+ case "ref":
28
+ const name = representatives[codeExp.ref] || codeExp.ref;
29
+ if (name.startsWith(":")) {
30
+ return prettyCode(codes, representatives, codes[name]);
31
+ } else {
32
+ return name;
33
+ }
34
+ case "product":
35
+ return `{${prettyCode_labels(codes, representatives, codeExp.product)}}`;
36
+ case "union":
37
+ return `<${prettyCode_labels(codes, representatives, codeExp.union)}>`;
38
+ case "vector":
39
+ return `[${representatives[codeExp.vector] || codeExp.vector}]`;
40
+ default:
41
+ return ":error";
42
+ }
43
+ };
44
+
45
+ function prettyRel (prettyCode, exp) {
46
+ "use strict";
47
+ const pretty = (exp) => {
48
+ switch (exp.op) {
49
+ case "vector":
50
+ return `[${exp.vector.map(pretty).join(", ")}]`;
51
+ case "union":
52
+ return `<${exp.union.map(pretty).join(", ")}>`;
53
+ case "ref":
54
+ return exp.ref;
55
+ case "identity":
56
+ return "()";
57
+ case "comp":
58
+ return exp.comp.map(pretty).join(" ");
59
+ case "str":
60
+ return `'${exp.str}'`;
61
+ case "int":
62
+ return exp.int;
63
+ case "dot":
64
+ if ("number" === typeof exp.dot) {
65
+ return `.${exp.dot}`;
66
+ } else if (nameRE.test(exp.dot)) {
67
+ return `.${exp.dot}`;
68
+ } else {
69
+ return `'${exp.dot}'`;
70
+ }
71
+ break;
72
+ case "code":
73
+ return `$${prettyCode({
74
+ code: "ref",
75
+ ref: exp.code,
76
+ })}`;
77
+ case "product":
78
+ return (function (labelled) {
79
+ return `{${labelled.join(", ")}}`;
80
+ })(
81
+ exp.product.map(function ({ label, exp }) {
82
+ if (nameRE.test(label)) {
83
+ return `${pretty(exp)} ${label}`;
84
+ } else {
85
+ return `${pretty(exp)} '${label}'`;
86
+ }
87
+ })
88
+ );
89
+ }
90
+ };
91
+ return pretty(exp);
92
+ };
93
+
94
+ export default { prettyCode, prettyRel };
95
+ export { prettyCode, prettyRel };
package/repl.mjs ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ import k from "./index.mjs";
4
+ import run from "./run.mjs";
5
+ import { prettyCode, prettyRel } from "./pretty.mjs";
6
+
7
+ import fs from "node:fs";
8
+
9
+ const splice = [].splice;
10
+
11
+ console.log("Very! experimental repl shell for 'k-language'...");
12
+
13
+ const help = function () {
14
+ console.log(" --h print help");
15
+ console.log(" --a print codes and relations");
16
+ console.log(" --c print codes");
17
+ console.log(" --r print rels");
18
+ return console.log(" --l file.k load 'file.k'");
19
+ };
20
+
21
+ help();
22
+
23
+ const re = /^[ \n\t]*(?:--l[ ]+)(.+[^ ])[ ]*?$/;
24
+
25
+ (function (val, buffer) {
26
+ return process.stdin.on("data", function (data) {
27
+ var e, file, first, i, kScript, last, len, line, ref, rest, results, todo;
28
+ [first, ...rest] = data.toString("utf8").split("\n");
29
+ buffer.push(first);
30
+ if (rest.length > 0) {
31
+ todo = buffer.join("");
32
+ (ref = rest), ([...rest] = ref), ([last] = splice.call(rest, -1));
33
+ buffer = [last];
34
+ todo = [todo, ...rest];
35
+ results = [];
36
+ for (i = 0, len = todo.length; i < len; i++) {
37
+ line = todo[i];
38
+ if (val === void 0) {
39
+ val = {};
40
+ }
41
+ if (line.match(/^[ \n\t]*(?:--h)?$/)) {
42
+ results.push(help());
43
+ } else if (line.match(re)) {
44
+ file = line.match(re)[1];
45
+ console.log(`-- loading file: ${file} ...`);
46
+ kScript = fs.readFileSync(file).toString();
47
+ val = k.compile(kScript)(val);
48
+ results.push(console.log(`=> ${JSON.stringify(val)}`));
49
+ } else if (line.match(/^[ \n\t]*(?:--a)?$/)) {
50
+ results.push(console.log(JSON.stringify(run.defs, " ", 2)));
51
+ } else if (line.match(/^[ \n\t]*(?:--r)?$/)) {
52
+ results.push(
53
+ console.log(
54
+ (function (defs, result) {
55
+ if (defs == null) {
56
+ return result;
57
+ }
58
+ return (function (prettyRel) {
59
+ var ref1, relExps, relName;
60
+ ref1 = defs.rels;
61
+ for (relName in ref1) {
62
+ relExps = ref1[relName];
63
+ result[relName] = relExps.map(prettyRel);
64
+ }
65
+ return result;
66
+ })(
67
+ prettyRel.bind(
68
+ null,
69
+ prettyCode.bind(null, defs.codes, defs.representatives)
70
+ )
71
+ );
72
+ })(run.defs, {})
73
+ )
74
+ );
75
+ } else if (line.match(/^[ \n\t]*(?:--c)?$/)) {
76
+ results.push(
77
+ console.log(
78
+ (function (defs, result) {
79
+ var codeExp, codeName, ref1;
80
+ if (defs == null) {
81
+ return result;
82
+ }
83
+ ref1 = defs.codes;
84
+ for (codeName in ref1) {
85
+ codeExp = ref1[codeName];
86
+ result[codeName] = prettyCode(
87
+ defs.codes,
88
+ defs.representatives,
89
+ codeExp
90
+ );
91
+ }
92
+ return result;
93
+ })(run.defs, {})
94
+ )
95
+ );
96
+ } else if (!line.match(/^[ \n\t]*(?:#.*)?$/)) {
97
+ try {
98
+ val = k.run(`${line} ()`, val);
99
+ results.push(console.log(`=> ${JSON.stringify(val)}`));
100
+ } catch (error) {
101
+ e = error;
102
+ console.error("ERROR:");
103
+ results.push(console.error(e));
104
+ }
105
+ } else {
106
+ results.push(void 0);
107
+ }
108
+ }
109
+ return results;
110
+ }
111
+ });
112
+ })({}, []);
package/run.mjs ADDED
@@ -0,0 +1,158 @@
1
+ const modulo = (a, b) => ((+a % (b = +b)) + b) % b;
2
+ const valid = (x) => (isNaN(x) ? undefined : x);
3
+
4
+ const builtin = {
5
+ "_log!": (arg) => {
6
+ console.error(`_log!: ${JSON.stringify(arg)}`);
7
+ return arg;
8
+ },
9
+ GT: (args) => {
10
+ const [last, ...rest] = args;
11
+ const [ok, _] = rest.reduce(
12
+ ([ok, last], x) => [ok && last > x, x],
13
+ [true, last]
14
+ );
15
+ if (ok) return args;
16
+ },
17
+ EQ: (args) => {
18
+ const [first, ...rest] = args;
19
+ const ok = rest.reduce((ok, x) => ok && first === x, true);
20
+ if (ok) return args;
21
+ },
22
+ PLUS: (args) => valid(args.reduce((res, x) => res + x, 0)),
23
+ TIMES: (args) => valid(args.reduce((res, x) => res * x, 1)),
24
+ DIV: ([x, y]) => {
25
+ const div = Math.floor(x / y);
26
+ const rem = modulo(x, y);
27
+ if (x === div * y + rem) return { div, rem };
28
+ },
29
+ FDIV: ([x, y]) => x / y,
30
+ CONCAT: (strs) => strs.join(""),
31
+ true: () => true,
32
+ false: () => false,
33
+ null: () => null,
34
+ toJSON: (x) => JSON.stringify(x),
35
+ fromJSON: (x) => JSON.parse(x),
36
+ CONS: ([x, y]) => [x, ...y],
37
+ SNOC: (x) => (x.length > 1 ? [x[0], x.slice(1)] : undefined),
38
+ toDateMsec: (x) => new Date(x).getTime(),
39
+ toDateStr: (x) => new Date(x).toISOString(),
40
+ };
41
+
42
+ const codes = {
43
+ int: (x) => Number.isInteger(x),
44
+ string: (x) => x instanceof String || "string" === typeof x,
45
+ bool: (x) => x === true || x === false,
46
+ };
47
+
48
+ function verify(code, value) {
49
+ if (code == null) {
50
+ // representatives = run.defs.representatives
51
+ // defCodes = JSON.stringify run.defs.codes
52
+ // console.log {code,value, representatives, defCodes}
53
+ return false;
54
+ }
55
+ switch (code.code) {
56
+ case "vector":
57
+ if (!Array.isArray(value)) return false;
58
+ return value.every((x) => verify(code.vector, x));
59
+ case "product":
60
+ if ("object" !== typeof value) return false;
61
+ else {
62
+ const fields = Object.keys(value);
63
+ if (fields.length !== Object.keys(code.product).length) return false;
64
+ return fields.every((label) =>
65
+ verify(code.product[label], value[label])
66
+ );
67
+ }
68
+ case "union":
69
+ if ("object" !== typeof value) return false;
70
+ else {
71
+ const fields = Object.keys(value);
72
+ if (fields.length !== 1) return false;
73
+ return verify(code.union[fields[0]], value[fields[0]]);
74
+ }
75
+ default: {
76
+ const c = run.defs.codes[run.defs.representatives[code]];
77
+ if (c != null) return verify(c, value);
78
+ return codes[code](value);
79
+ }
80
+ }
81
+ }
82
+
83
+ function run(exp, value) {
84
+ "use strict";
85
+ if (value === undefined) return;
86
+ switch (exp.op) {
87
+ case "code":
88
+ if (verify(exp.code, value)) {
89
+ return value;
90
+ }
91
+ break;
92
+ case "identity":
93
+ return value;
94
+ case "str":
95
+ case "int":
96
+ return exp[exp.op];
97
+ case "ref":
98
+ const defn = run.defs.rels[exp.ref];
99
+ if (defn != null) {
100
+ return run(defn[defn.length - 1], value);
101
+ }
102
+ return builtin[exp.ref](value);
103
+ case "dot":
104
+ if (typeof value === "string") {
105
+ if (value == exp.dot) return {};
106
+ return;
107
+ }
108
+ if (/^(0|([1-9]\d*))$/.test(value)) {
109
+ if (value == exp.dot) return {};
110
+ return;
111
+ }
112
+ if (value === true) {
113
+ if (exp.dot == "true") return {};
114
+ return;
115
+ }
116
+ if (value === false) {
117
+ if (exp.dot == "false") return {};
118
+ return;
119
+ }
120
+ return value[exp.dot];
121
+ case "comp":
122
+ return exp.comp.reduce((value, exp) => {
123
+ if (value !== undefined) return run(exp, value);
124
+ }, value);
125
+ case "union":
126
+ for (let i = 0, len = exp.union.length; i < len; i++) {
127
+ const result = run(exp.union[i], value);
128
+ if (result !== undefined) {
129
+ return result;
130
+ }
131
+ }
132
+ return;
133
+ case "vector": {
134
+ const result = [];
135
+ for (let i = 0, len = exp.vector.length; i < len; i++) {
136
+ const r = run(exp.vector[i], value);
137
+ if (r === undefined) return;
138
+ result.push(r);
139
+ }
140
+ return result;
141
+ }
142
+ case "product": {
143
+ const result = {};
144
+ for (let i = 0, len = exp.product.length; i < len; i++) {
145
+ const { label, exp: e } = exp.product[i];
146
+ const r = run(e, value);
147
+ if (r === undefined) return;
148
+ result[label] = r;
149
+ }
150
+ return result;
151
+ }
152
+ default:
153
+ return console.error(exp.op);
154
+ }
155
+ }
156
+
157
+ export default run;
158
+ export { run };
@@ -0,0 +1,133 @@
1
+ const rels = {};
2
+ const codes = {
3
+ "{}": {
4
+ code: "product",
5
+ product: {},
6
+ },
7
+ };
8
+
9
+ const identity = {
10
+ op: "identity",
11
+ };
12
+
13
+ function is_identity_rel(rel) {
14
+ return rel.op === "identity";
15
+ }
16
+
17
+ function is_constant_rel(rel) {
18
+ switch (rel.op) {
19
+ case "int":
20
+ case "str":
21
+ return true;
22
+ case "vector":
23
+ return Object.values(rel[rel.op]).every(is_constant_rel);
24
+ case "product":
25
+ return Object.values(rel[rel.op]).every(({ exp }) =>
26
+ is_constant_rel(exp)
27
+ );
28
+ default:
29
+ return false;
30
+ }
31
+ }
32
+
33
+ function is_empty_rel(rel) {
34
+ return rel.op === "union" && rel.union.length === 0;
35
+ }
36
+
37
+ function is_full_rel(rel) {
38
+ if (is_constant_rel(rel)) return true;
39
+ switch (rel.op) {
40
+ case "int":
41
+ case "str":
42
+ case "identity":
43
+ return true;
44
+ case "comp":
45
+ return rel.comp.every(is_full_rel);
46
+ case "vector":
47
+ return Object.values(rel[rel.op]).every(is_full_rel);
48
+ case "product":
49
+ return Object.values(rel[rel.op]).every(({ exp }) => is_full_rel(exp));
50
+ default:
51
+ return false;
52
+ }
53
+ }
54
+
55
+ function comp_first(e1, e2) {
56
+ if (is_identity_rel(e1)) return e2;
57
+ if (is_identity_rel(e2)) return e1;
58
+ if (is_empty_rel(e1)) return e1;
59
+ if (is_empty_rel(e2)) return e2;
60
+ if (e1.op === "comp" && e2.op === "comp")
61
+ return {
62
+ op: "comp",
63
+ comp: [].concat(e1.comp, e2.comp),
64
+ };
65
+ if (e1.op === "comp")
66
+ return {
67
+ op: "comp",
68
+ comp: [].concat(e1.comp, [e2]),
69
+ };
70
+ if (e2.op === "comp")
71
+ return {
72
+ op: "comp",
73
+ comp: [].concat([e1], e2.comp),
74
+ };
75
+ return {
76
+ op: "comp",
77
+ comp: [e1, e2],
78
+ };
79
+ }
80
+
81
+ function comp (e1, e2) {
82
+ const result = comp_first(e1, e2);
83
+ if (result.op !== "comp") return result;
84
+ result.comp = result.comp.reduceRight(
85
+ (c, e) => (c[0] && is_constant_rel(c[0]) && is_full_rel(e) ? c : [e, ...c]),
86
+ []
87
+ );
88
+ return result;
89
+ }
90
+
91
+ function union (rels) {
92
+ const list = [];
93
+ label: for (const rel of rels) {
94
+ const new_rels = rel.op === "union" ? rel.union : [rel];
95
+ for (const new_rel of new_rels) {
96
+ list.push(new_rel);
97
+ if (is_full_rel(new_rel)) break label;
98
+ }
99
+ }
100
+ if (list.length === 1) return list[0];
101
+ return {
102
+ op: "union",
103
+ union: list,
104
+ };
105
+ };
106
+
107
+ function as_ref (codeExp) {
108
+ if (codeExp.code === "ref") return codeExp.ref;
109
+ const newName = `:${Object.keys(codes).length}`;
110
+ codes[newName] = codeExp;
111
+ return newName;
112
+ };
113
+
114
+ function add_rel (name, rel) {
115
+ if (rels[name] == null) rels[name] = [];
116
+ rels[name].push(rel);
117
+ };
118
+
119
+ function add_code (name, code) {
120
+ codes[name] = code;
121
+ }
122
+
123
+ export default {
124
+ identity,
125
+ comp,
126
+ union,
127
+ rels,
128
+ codes,
129
+ add_rel,
130
+ add_code,
131
+ as_ref,
132
+ };
133
+ export { identity, comp, union, rels, codes, add_rel, add_code, as_ref };
package/test.mjs ADDED
@@ -0,0 +1,143 @@
1
+ import k from "./index.mjs";
2
+
3
+ [
4
+ ["ANYTHING...", "()"],
5
+ ["ANYTHING...", '{"ala" name, 23 age}'],
6
+ [
7
+ {
8
+ year: 2002,
9
+ age: 19,
10
+ },
11
+ "[.year, .age]",
12
+ ],
13
+ ["duplicate me", "[(), ()]"],
14
+ ["nesting", "[[[()]]]"],
15
+ ["nesting and accessing", "[[()]] {() nested, .0.0 val}"],
16
+ [
17
+ {
18
+ test: "parse integer",
19
+ },
20
+ "0000",
21
+ ],
22
+ [
23
+ {
24
+ x: 3,
25
+ y: 4,
26
+ },
27
+ "[.y,.x] PLUS",
28
+ ],
29
+ ].map(function ([data, script]) {
30
+ console.log(`k_expression = '${script}';`);
31
+ console.log(`k.run(k_expression,${JSON.stringify(data)});`);
32
+ console.log(`// RETURNS: ${JSON.stringify(k.run(script, data))}`);
33
+ return console.log("");
34
+ });
35
+
36
+ let k_expression =
37
+ '{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}';
38
+
39
+ let k_fn = k.compile(k_expression);
40
+
41
+ console.log(`var k_fn = k.compile('${k_expression}');`);
42
+
43
+ console.log("");
44
+
45
+ [
46
+ {
47
+ age: 23,
48
+ name: "Emily",
49
+ },
50
+ {
51
+ age: 16,
52
+ name: "Katrina",
53
+ },
54
+ {
55
+ age: 2,
56
+ name: "Mark",
57
+ },
58
+ ].map(function (data) {
59
+ console.log(`k_fn(${JSON.stringify(data)});`);
60
+ console.log(`// RETURNS: ${JSON.stringify(k_fn(data))}`);
61
+ return console.log("");
62
+ });
63
+
64
+ k_expression = "$t = < i: int, t: [ t ] > ; <$t, $int>";
65
+
66
+ k_fn = k.compile(k_expression);
67
+
68
+ console.log(`var k_fn = k.compile('${k_expression}');`);
69
+
70
+ console.log("");
71
+
72
+ [
73
+ 1,
74
+ {
75
+ i: 1,
76
+ },
77
+ [
78
+ {
79
+ i: 2,
80
+ },
81
+ {
82
+ i: 3,
83
+ },
84
+ {
85
+ t: [],
86
+ },
87
+ ],
88
+ {
89
+ t: [
90
+ {
91
+ i: 2,
92
+ },
93
+ {
94
+ i: 3,
95
+ },
96
+ {
97
+ t: [],
98
+ },
99
+ ],
100
+ },
101
+ ].map(function (data) {
102
+ console.log(`k_fn(${JSON.stringify(data)});`);
103
+ console.log(`// RETURNS: ${JSON.stringify(k_fn(data))}`);
104
+ return console.log("");
105
+ });
106
+
107
+ k_expression = "$ < < [ int ] ints, [ bool ] bools > list, string None>";
108
+
109
+ k_fn = k.compile(k_expression);
110
+
111
+ console.log(`var k_fn = k.compile('${k_expression}');`);
112
+
113
+ console.log("");
114
+
115
+ [
116
+ {
117
+ None: "None",
118
+ },
119
+ {
120
+ list: {
121
+ ints: [],
122
+ },
123
+ },
124
+ {
125
+ list: {
126
+ ints: [1, 2, 3],
127
+ },
128
+ },
129
+ {
130
+ list: {
131
+ bools: [true, false],
132
+ },
133
+ },
134
+ {
135
+ list: {
136
+ bools: [true, 0],
137
+ },
138
+ },
139
+ ].map(function (data) {
140
+ console.log(`k_fn(${JSON.stringify(data)});`);
141
+ console.log(`// RETURNS: ${JSON.stringify(k_fn(data))}`);
142
+ return console.log("");
143
+ });