@fraczak/k 2.0.0 → 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/README.md +221 -42
- package/codes.mjs +0 -1
- package/index.mjs +0 -3
- package/k.mjs +29 -42
- package/package.json +1 -1
- package/run.mjs +16 -0
- package/INTRO.md +0 -160
package/README.md
CHANGED
|
@@ -10,31 +10,177 @@ From javascript:
|
|
|
10
10
|
console.log([{name:"x"},{nom:"y"},{}].map(fn));
|
|
11
11
|
// returns: [ "x", "y", "?" ]
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
defines a __partial function__, i.e., a function which may fail
|
|
15
|
-
for some input. `k`-expressions can be combined to build
|
|
16
|
-
other `k`-expressions in three ways:
|
|
13
|
+
---
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
## k - the way of building and manipulating JSON-like data
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
Technically, `k` is a notation for defining **first-order partial functions**.
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
An example of a _partial function_ is the _projection_, e.g., "`.toto`",
|
|
20
|
+
which maps an object to its property named `toto`, or it is not defined if
|
|
21
|
+
the property doesn't exist. E.g.,
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
1 .toto :
|
|
24
|
+
2 {"toto": 5, "titi": 10} --> 5
|
|
25
|
+
3 {"titi": 10} ... undefined // it is not a value!
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
> Note: The above 3 lines should be read as follows. A `k`-expression
|
|
28
|
+
> is printed in the first line (before "`:`"). The following lines
|
|
29
|
+
> are examples of the function defined by the `k`-expression applayed
|
|
30
|
+
> to JSON values (first part of each line). If the function for the
|
|
31
|
+
> value is defined, then the result is printed after "`-->`" (line 2 in
|
|
32
|
+
> the above example). If the function is not defined for the value,
|
|
33
|
+
> then "`... undefined`" is printed (line 3).
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
---
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
### Combining "partial functions"
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
There are three ways of combining functions:
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
1. **composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
|
|
42
|
+
nested field.
|
|
43
|
+
|
|
44
|
+
(.toto .titi) :
|
|
45
|
+
{"toto": {"titi": 10}} --> 10
|
|
46
|
+
{"toto": 10 } ... undefined
|
|
47
|
+
{} ... undefined
|
|
48
|
+
|
|
49
|
+
2. **merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
|
|
50
|
+
`toto` if present; otherwise extracts `titi`.
|
|
51
|
+
|
|
52
|
+
<.toto, .titi> :
|
|
53
|
+
{"toto": 5, "titi": 10} --> 5
|
|
54
|
+
{"titi": 10 } --> 10
|
|
55
|
+
{} ... undefined
|
|
56
|
+
|
|
57
|
+
3. **product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
|
|
58
|
+
.titi TITI}` extracts two fields and builds a record out of them.
|
|
59
|
+
|
|
60
|
+
{.toto TOTO, .titi TITI} :
|
|
61
|
+
{"toto": 5, "titi": 10, "x": 3} --> {"TOTO": 5, "TITI": 10}
|
|
62
|
+
{"titi": 10 } ... undefined
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
**QUIZ**: What is:
|
|
67
|
+
|
|
68
|
+
- empty composition: `()` ?
|
|
69
|
+
- empty merge: `<>` ?
|
|
70
|
+
- empty product: `{}` ?
|
|
71
|
+
- `{{{{} s} s} s}` ?
|
|
72
|
+
- `({{{() a} b} c} .c .b .a)` ?
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### Syntactic sugar
|
|
77
|
+
|
|
78
|
+
- parenthesis can be omitted, except for the empty composition `()`,
|
|
79
|
+
- dot (`.`) in "projection" acts as a separator so the space around it can be omitted.
|
|
80
|
+
|
|
81
|
+
For example, `[(.toto .titi (.0 .1))]` can be written as `[.toto.titi.0.1]`.
|
|
82
|
+
|
|
83
|
+
Comments can be introduced by `//`, `--`, `%`, or `#` and extends to
|
|
84
|
+
the end of line. Multiline `C`-like comments, `/* ... */`, are also
|
|
85
|
+
supported.
|
|
86
|
+
|
|
87
|
+
### Basic extensions
|
|
88
|
+
|
|
89
|
+
#### Constants, i.e., literals for `strings`, `integers`, `booleans`, and `null`
|
|
90
|
+
|
|
91
|
+
A constant defines a function which ignores its argument and produces
|
|
92
|
+
the constant value. E.g.:
|
|
93
|
+
|
|
94
|
+
{123 int, "kScript" str, true bool, null null} :
|
|
95
|
+
"any" --> {"int":123,"str":"kScript","bool":true,"null":null}
|
|
96
|
+
|
|
97
|
+
#### Vector product
|
|
98
|
+
|
|
99
|
+
Vector product can be seen as an abbreviation for product whose field
|
|
100
|
+
names are integers starting from zero. E.g., `{.toto 0, .titi 1, 123 2}`
|
|
101
|
+
can be written as `[.toto, .titi, 123]`.
|
|
102
|
+
|
|
103
|
+
[.toto, .titi, 12] :
|
|
104
|
+
{"toto": 5, "titi": 10 } --> [5, 10, 12]
|
|
105
|
+
|
|
106
|
+
.1 :
|
|
107
|
+
["A","B","C"] --> "B"
|
|
108
|
+
["a"] ... undefined
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### Pragmatic extensions, aka "standard library"
|
|
113
|
+
|
|
114
|
+
- `GT` : -- identity for lists of decreasing elements; undefined otherwise
|
|
115
|
+
|
|
116
|
+
[4,3] --> [4,3]
|
|
117
|
+
[3,4] ... undefined
|
|
118
|
+
[] --> []
|
|
119
|
+
[4,3,0] --> [4,3,0]
|
|
120
|
+
|
|
121
|
+
- `EQ` : -- identity for lists of equal elements; undefined otherwise
|
|
122
|
+
|
|
123
|
+
[4,4] --> [4,4]
|
|
124
|
+
[4,5] ... undefined
|
|
125
|
+
[4,4,4] --> [4,4,4]
|
|
126
|
+
[] --> []
|
|
127
|
+
|
|
128
|
+
- `{PLUS plus, TIMES times}` :
|
|
129
|
+
|
|
130
|
+
[1,2] --> {"plus":3,"times":2}
|
|
131
|
+
[2,2,2] --> {"plus":6,"times":8}
|
|
132
|
+
[] --> {"plus":0,"times":1}
|
|
133
|
+
|
|
134
|
+
- `CONCAT` :
|
|
135
|
+
|
|
136
|
+
["a","bc","d"] --> "abcd"
|
|
137
|
+
|
|
138
|
+
- `toJSON` :
|
|
139
|
+
|
|
140
|
+
{"a": 12} --> "{\"a\":12}"
|
|
141
|
+
|
|
142
|
+
- `fromJSON` :
|
|
143
|
+
|
|
144
|
+
"{\"a\":12}" --> {"a":12}
|
|
145
|
+
|
|
146
|
+
- other predefined parial functions are: `DIV`, `FDIV`, `CONS`, `SNOC`, `toDateMsec`,
|
|
147
|
+
`toDateStr`, and `_log!`.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### Function definitions
|
|
152
|
+
|
|
153
|
+
dec = [(),-1] PLUS;
|
|
154
|
+
max = <SNOC [.0, .1 max] <GT.0, .1>, .0> ;
|
|
155
|
+
factorial = < [(),0] GT .0 [dec factorial, ()] TIMES, 1 >;
|
|
156
|
+
|
|
157
|
+
### Codes (_types_)
|
|
158
|
+
|
|
159
|
+
_Codes_ (prefixed by `$`) can be defined by taged union and product. E.g.:
|
|
160
|
+
|
|
161
|
+
$nat = <nat 1, {} 0>;
|
|
162
|
+
$pair = {nat x, nat y};
|
|
163
|
+
|
|
164
|
+
suc = {$nat 1};
|
|
165
|
+
add = $pair <{.x.1 x, .y suc y} add, .y>;
|
|
166
|
+
|
|
167
|
+
#### Basic extension codes
|
|
168
|
+
|
|
169
|
+
Since _basic extension_ introduces integers, booleans, and strings, there are three
|
|
170
|
+
predefined types: `int`, `bool`, and `string`. A vector product code can also be defined
|
|
171
|
+
by `[ codeExp ]`. All members of the vector are the same code. E.g.,
|
|
172
|
+
|
|
173
|
+
$intVector = [ int ];
|
|
174
|
+
$boolVector = [ bool ];
|
|
175
|
+
$tree = [ tree ];
|
|
176
|
+
|
|
177
|
+
emptyList? = $intVector $[ string ]; -- as only an empty vector can be a vector
|
|
178
|
+
-- of integers and a vector of strings
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
1. projection:
|
|
38
184
|
|
|
39
185
|
.x
|
|
40
186
|
."field name"
|
|
@@ -43,7 +189,7 @@ other `k`-expressions in three ways:
|
|
|
43
189
|
The function is defined only if its argument is a structure with
|
|
44
190
|
the field (or a vector with the index).
|
|
45
191
|
|
|
46
|
-
|
|
192
|
+
2. _constants_, literals for Strings, Booleans, and
|
|
47
193
|
Integers. Examples:
|
|
48
194
|
|
|
49
195
|
"a string"
|
|
@@ -52,14 +198,12 @@ the field (or a vector with the index).
|
|
|
52
198
|
false
|
|
53
199
|
null
|
|
54
200
|
|
|
55
|
-
|
|
56
|
-
`TIMES`, `DIV`, `CONCAT`, `toJSON`, `fromJSON`, `CONS`,
|
|
57
|
-
`SNOC`, `toDateMsec`, `toDateStr`, and `_log!`. For example:
|
|
201
|
+
3. "built-in" functions:
|
|
58
202
|
|
|
59
|
-
[1, 2, 3] PLUS
|
|
60
|
-
[4, 4] TIMES toJSON
|
|
61
|
-
[3, 2] GT
|
|
62
|
-
[3, 4] GT ...
|
|
203
|
+
[1, 2, 3] PLUS -- integer constant function 6
|
|
204
|
+
[4, 4] TIMES toJSON -- string constant function "16"
|
|
205
|
+
[3, 2] GT -- vector constant function [3, 2]
|
|
206
|
+
[3, 4] GT -- ... undefined
|
|
63
207
|
|
|
64
208
|
A more interesting example could be:
|
|
65
209
|
|
|
@@ -69,7 +213,7 @@ the field (or a vector with the index).
|
|
|
69
213
|
|
|
70
214
|
[3,8] < GT .0, .1 > --> 8
|
|
71
215
|
|
|
72
|
-
|
|
216
|
+
### User defined functions
|
|
73
217
|
|
|
74
218
|
`k`-expression can be prefixed by function definitions. E.g.:
|
|
75
219
|
|
|
@@ -84,22 +228,22 @@ the field (or a vector with the index).
|
|
|
84
228
|
Another example could be finding the biggest (max) value in a vector:
|
|
85
229
|
|
|
86
230
|
max = <
|
|
87
|
-
SNOC
|
|
88
|
-
[.0, .1 max]
|
|
89
|
-
<GT .0, .1
|
|
90
|
-
|
|
91
|
-
.0
|
|
231
|
+
SNOC # [x0, x1, x2, ...] --> [x0, [x1, x2, ...]]
|
|
232
|
+
[.0, .1 max] # [x0, [x1, x2, ...]] --> [x0, max(x1,x2,...)], i.e., recursive call
|
|
233
|
+
<GT .0, .1> # if x0 > max(x1,x2,...) then x0 else max(x1,x2,...)
|
|
234
|
+
, # when SNOC is not defined, i.e., if the input vector has one element:
|
|
235
|
+
.0 # [x0] --> x0
|
|
92
236
|
>;
|
|
93
237
|
max
|
|
94
238
|
|
|
95
|
-
|
|
239
|
+
### Value encodings (_codes_)
|
|
96
240
|
|
|
97
241
|
There are three predefined value encodings: `int`, `string`, and
|
|
98
242
|
`bool`. The language supports `code`-expressions:
|
|
99
243
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
244
|
+
- product, e.g., `{int x, int y, bool flag}`
|
|
245
|
+
- disjoint union, e.g., `<{} true, {} false>`
|
|
246
|
+
- vector, e.g., `[ int ]` (all elements of the vector use the same
|
|
103
247
|
encoding)
|
|
104
248
|
|
|
105
249
|
One can define recursive codes. E.g.:
|
|
@@ -121,7 +265,9 @@ within `k`-expression is again prefixed by `$`.
|
|
|
121
265
|
> $ int;
|
|
122
266
|
height
|
|
123
267
|
|
|
124
|
-
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## `k` (a command-line JSON processor using `k` syntax)
|
|
125
271
|
|
|
126
272
|
There is a wrapper, `./node_modules/.bin/k` , which makes it easy to
|
|
127
273
|
run the language from command line.
|
|
@@ -131,7 +277,7 @@ run the language from command line.
|
|
|
131
277
|
Usage: ./node_modules/.bin/k ( k-expr | -k k-file) [ -1 ] [ json-file ]
|
|
132
278
|
E.g., cat '{"a": 10}' | ./node_modules/.bin/k '[(),()]'
|
|
133
279
|
|
|
134
|
-
|
|
280
|
+
For example:
|
|
135
281
|
|
|
136
282
|
1. One `k`-expression with one `json`-object:
|
|
137
283
|
|
|
@@ -149,6 +295,8 @@ run the language from command line.
|
|
|
149
295
|
{"z": 123} --> {"x&y":"no x nor y"}
|
|
150
296
|
^D - to interrupt
|
|
151
297
|
|
|
298
|
+
If the input is a multiline json object, we need to add `-1` to the command-line options.
|
|
299
|
+
|
|
152
300
|
3. If the `k`-expression is long, it can be put in a file, e.g.:
|
|
153
301
|
|
|
154
302
|
> cat test.k
|
|
@@ -187,7 +335,36 @@ run the language from command line.
|
|
|
187
335
|
{"z": 123}
|
|
188
336
|
####################################################
|
|
189
337
|
|
|
190
|
-
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### Short comparaison with `jq` tutorial examples: <https://stedolan.github.io/jq/tutorial/>
|
|
341
|
+
|
|
342
|
+
1.
|
|
343
|
+
curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.'
|
|
344
|
+
curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | k '()' -1
|
|
345
|
+
2.
|
|
346
|
+
curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.[0]'
|
|
347
|
+
curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | k '.0' -1
|
|
348
|
+
3.
|
|
349
|
+
jq '.[0] | {message: .commit.message, name: .commit.committer.name}'
|
|
350
|
+
k '.0 {.commit.message message, .commit.committer.name name}' -1
|
|
351
|
+
4. note: `k`-expression defines a partial function yielding a single json object, i.e., as far as `k` is concerned,
|
|
352
|
+
examples 4 and 5 of the `jq` tutorial are equivalent.
|
|
353
|
+
5.
|
|
354
|
+
jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'
|
|
355
|
+
k 'f = .commit {.message message, .committer.name name}; %\
|
|
356
|
+
map_f = <SNOC [.0 f,.1 map_f] CONS, [.0 f], []>; %\
|
|
357
|
+
map_f' -1
|
|
358
|
+
6.
|
|
359
|
+
jq '[.[] | {message: .commit.message, name: .commit.committer.name, parents: [.parents[].html_url]}]'
|
|
360
|
+
k 'map_html_url = <SNOC [.0 .html_url, .1 map_html_url] CONS, [.0 .html_url], []>; %\
|
|
361
|
+
f = {.commit.message message, .commit.committer.name name, .parents map_html_url parents}; %\
|
|
362
|
+
map_f = <SNOC [.0 f, .1 map_f] CONS, [.0 f] , []>; %\
|
|
363
|
+
map_f' -1
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## k-REPL (Read-Evaluate-Print Loop)
|
|
191
368
|
|
|
192
369
|
Also there is a REPL, `./node_modules/.bin/k-repl`, which acts like a toy
|
|
193
370
|
shell for the language. E.g.:
|
|
@@ -202,11 +379,13 @@ shell for the language. E.g.:
|
|
|
202
379
|
inc inc inc inc
|
|
203
380
|
=> 7
|
|
204
381
|
|
|
205
|
-
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Using `k` from `javascript`
|
|
206
385
|
|
|
207
|
-
|
|
386
|
+
import k from "@fraczak/k";
|
|
208
387
|
|
|
209
|
-
k_expression = '()';
|
|
388
|
+
let k_expression = '()';
|
|
210
389
|
k.run(k_expression,"ANYTHING...");
|
|
211
390
|
// RETURNS: "ANYTHING..."
|
|
212
391
|
|
|
@@ -249,7 +428,7 @@ shell for the language. E.g.:
|
|
|
249
428
|
k_fn({"age":2,"name":"Mark"});
|
|
250
429
|
// RETURNS: {"nom":"Mark","age":"enfant"}
|
|
251
430
|
|
|
252
|
-
|
|
431
|
+
k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
|
|
253
432
|
|
|
254
433
|
k_fn(1);
|
|
255
434
|
// RETURNS: 1
|
|
@@ -263,7 +442,7 @@ shell for the language. E.g.:
|
|
|
263
442
|
k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
|
|
264
443
|
// RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
|
|
265
444
|
|
|
266
|
-
|
|
445
|
+
k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
|
|
267
446
|
|
|
268
447
|
k_fn({"None":"None"});
|
|
269
448
|
// RETURNS: {"None":"None"}
|
package/codes.mjs
CHANGED
package/index.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { parse } from "./parser.mjs";
|
|
2
|
-
|
|
3
2
|
import run from "./run.mjs";
|
|
4
|
-
|
|
5
3
|
import t from "./codes.mjs";
|
|
6
4
|
|
|
7
5
|
function finalize(codes) {
|
|
@@ -31,5 +29,4 @@ function runScriptOnData(script, data) {
|
|
|
31
29
|
runScriptOnData.doc = "Run 'script' (string) on 'data': (script,data) -> data";
|
|
32
30
|
|
|
33
31
|
export default { compile, run: runScriptOnData, parse };
|
|
34
|
-
|
|
35
32
|
export { compile, runScriptOnData as run, parse };
|
package/k.mjs
CHANGED
|
@@ -9,7 +9,7 @@ const prog = process.argv[1];
|
|
|
9
9
|
|
|
10
10
|
let kScript, jsonStream, oneJson;
|
|
11
11
|
|
|
12
|
-
({ kScript, jsonStream, oneJson } = (
|
|
12
|
+
({ kScript, jsonStream, oneJson } = ((oneJson, args) => {
|
|
13
13
|
let e;
|
|
14
14
|
try {
|
|
15
15
|
kScript = (function (arg) {
|
|
@@ -44,49 +44,36 @@ let kScript, jsonStream, oneJson;
|
|
|
44
44
|
})(false, process.argv.slice(2)));
|
|
45
45
|
|
|
46
46
|
if (oneJson) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
e = error;
|
|
59
|
-
return console.error(e);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
})([]);
|
|
47
|
+
const buffer = [];
|
|
48
|
+
jsonStream.on("data", (data) => buffer.push(data));
|
|
49
|
+
jsonStream.on("end", () => {
|
|
50
|
+
try {
|
|
51
|
+
console.log(JSON.stringify(kScript(JSON.parse(buffer.join(""))), null, 2));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(error);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
63
56
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} catch (error) {
|
|
81
|
-
e = error;
|
|
82
|
-
console.error(`Problem [line ${line}]: '${json}'`);
|
|
83
|
-
console.error(e);
|
|
84
|
-
}
|
|
57
|
+
let buffer = [];
|
|
58
|
+
let line = 0;
|
|
59
|
+
jsonStream.on("data", (data) => {
|
|
60
|
+
const [first, ...rest] = data.toString("utf8").split("\n");
|
|
61
|
+
buffer.push(first);
|
|
62
|
+
if (rest.length > 0) {
|
|
63
|
+
const todo = buffer.join("");
|
|
64
|
+
const last = rest.pop();
|
|
65
|
+
buffer = [last];;
|
|
66
|
+
for (const exp of [todo, ...rest]) {
|
|
67
|
+
if (!exp.match(/^[ \n\t]*(?:#.*)?$/)) {
|
|
68
|
+
try {
|
|
69
|
+
console.log(JSON.stringify(kScript(JSON.parse(exp))));
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`Problem [line ${line}]: '${exp}'`);
|
|
72
|
+
console.error(error);
|
|
85
73
|
}
|
|
86
|
-
results.push((line = line + 1));
|
|
87
74
|
}
|
|
88
|
-
|
|
75
|
+
line++;
|
|
89
76
|
}
|
|
90
|
-
}
|
|
91
|
-
})
|
|
77
|
+
}
|
|
78
|
+
});
|
|
92
79
|
}
|
package/package.json
CHANGED
package/run.mjs
CHANGED
|
@@ -101,6 +101,22 @@ function run(exp, value) {
|
|
|
101
101
|
}
|
|
102
102
|
return builtin[exp.ref](value);
|
|
103
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
|
+
}
|
|
104
120
|
return value[exp.dot];
|
|
105
121
|
case "comp":
|
|
106
122
|
return exp.comp.reduce((value, exp) => {
|
package/INTRO.md
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# k - the way of building and manipulating JSON-like data
|
|
2
|
-
|
|
3
|
-
Technically, `k` is a notation for defining __first-order partial functions__.
|
|
4
|
-
|
|
5
|
-
An example of a _partial function_ is the _projection_, e.g., "`.toto`",
|
|
6
|
-
which maps an object to its property named `toto`, or it is not defined if
|
|
7
|
-
the property doesn't exist. E.g.,
|
|
8
|
-
|
|
9
|
-
.toto :
|
|
10
|
-
{"toto": 5, "titi": 10} --> 5
|
|
11
|
-
{"titi": 10} ... undefined // it is not a value!
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Combining "partial functions"
|
|
16
|
-
|
|
17
|
-
There are three ways of combining functions:
|
|
18
|
-
|
|
19
|
-
1. __composition__: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
|
|
20
|
-
nested field.
|
|
21
|
-
|
|
22
|
-
(.toto .titi) :
|
|
23
|
-
{"toto": {"titi": 10}} --> 10
|
|
24
|
-
{"toto": 10 } ... undefined
|
|
25
|
-
{} ... undefined
|
|
26
|
-
|
|
27
|
-
2. __merge__: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
|
|
28
|
-
`toto` if present; otherwise extracts `titi`.
|
|
29
|
-
|
|
30
|
-
<.toto, .titi> :
|
|
31
|
-
{"toto": 5, "titi": 10} --> 5
|
|
32
|
-
{"titi": 10 } --> 10
|
|
33
|
-
{} ... undefined
|
|
34
|
-
|
|
35
|
-
3. __product__: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
|
|
36
|
-
.titi TITI}` extracts two fields and builds a record out of them.
|
|
37
|
-
|
|
38
|
-
{.toto TOTO, .titi TITI} :
|
|
39
|
-
{"toto": 5, "titi": 10} --> {"TOTO": 5, "TITI": 10}
|
|
40
|
-
{"titi": 10 } ... undefined
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
__QUIZ__: What is:
|
|
45
|
-
|
|
46
|
-
- empty composition: `()` ?
|
|
47
|
-
- empty merge: `<>` ?
|
|
48
|
-
- empty product: `{}` ?
|
|
49
|
-
- `{{{{} s} s} s}` ?
|
|
50
|
-
- `({{{() a} b} c} .c .b .a)` ?
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
#### Syntactic sugar:
|
|
55
|
-
|
|
56
|
-
- parenthesis can be omitted, except for the empty composition `()`,
|
|
57
|
-
- dot (`.`) in "projection" acts as a separator so the space around it can be omitted.
|
|
58
|
-
|
|
59
|
-
For example, `[(.toto .titi (.0 .1))]` can be written as `[.toto.titi.0.1]`.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
## Basic extensions
|
|
63
|
-
|
|
64
|
-
### Constants, i.e., literals for `strings`, `integers`, `booleans`, and `null`
|
|
65
|
-
|
|
66
|
-
A constant defines a function which ignores its argument and produces
|
|
67
|
-
the constant value. E.g.:
|
|
68
|
-
|
|
69
|
-
{123 int, "kScript" str, true bool, null null} :
|
|
70
|
-
<any> --> {"int":123,"str":"kScript","bool":true,"null":null}
|
|
71
|
-
|
|
72
|
-
### Vector product
|
|
73
|
-
|
|
74
|
-
Vector product can be used as an abbreviation for product whose field
|
|
75
|
-
names are integers starting from zero. E.g., `{.toto "0", .titi "1",
|
|
76
|
-
123 "2"}` can be written as `[.toto, .titi, 123]`.
|
|
77
|
-
|
|
78
|
-
[.toto, .titi, 12] :
|
|
79
|
-
{"toto": 5, "titi": 10 } --> [5, 10, 12] // {"0":5,"1":10,"2":12}
|
|
80
|
-
|
|
81
|
-
.1 :
|
|
82
|
-
["A","B","C"] --> "B"
|
|
83
|
-
["a"] ... undefined
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Pragmatic extensions, aka "standard library"
|
|
87
|
-
|
|
88
|
-
- `GT` : -- identity for lists of decreasing elements; undefined otherwise
|
|
89
|
-
|
|
90
|
-
[4,3] --> [4,3]
|
|
91
|
-
[3,4] ... undefined
|
|
92
|
-
[] --> []
|
|
93
|
-
[4,3,0] --> [4,3,0]
|
|
94
|
-
|
|
95
|
-
- `EQ` : -- identity for lists of equal elements; undefined otherwise
|
|
96
|
-
|
|
97
|
-
[4,4] --> [4,4]
|
|
98
|
-
[4,5] ... undefined
|
|
99
|
-
[4,4,4] --> [4,4,4]
|
|
100
|
-
[] --> []
|
|
101
|
-
|
|
102
|
-
- `{PLUS plus, TIMES times}` :
|
|
103
|
-
|
|
104
|
-
[1,2] --> {"plus":3,"times":2}
|
|
105
|
-
[2,2,2] --> {"plus":6,"times":8}
|
|
106
|
-
[] --> {"plus":0,"times":1}
|
|
107
|
-
|
|
108
|
-
- `CONCAT` :
|
|
109
|
-
|
|
110
|
-
["a","bc","d"] --> "abcd"
|
|
111
|
-
|
|
112
|
-
- `toJSON` :
|
|
113
|
-
|
|
114
|
-
{"a": 12} --> "{\"a\":12}"
|
|
115
|
-
|
|
116
|
-
- `fromJSON` :
|
|
117
|
-
|
|
118
|
-
"{\"a\":12}" --> {"a":12}
|
|
119
|
-
|
|
120
|
-
- other predefined parial functions are: `DIV`, `FDIV`, `CONS`, `SNOC`, `toDateMsec`,
|
|
121
|
-
`toDateStr`, and `_log!`.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Recursive definitions
|
|
126
|
-
|
|
127
|
-
-- factorial.k
|
|
128
|
-
dec = [(),-1] PLUS;
|
|
129
|
-
zero? = [(),0] EQ 0;
|
|
130
|
-
factorial = <
|
|
131
|
-
zero? 1,
|
|
132
|
-
[dec factorial, ()] TIMES
|
|
133
|
-
>;
|
|
134
|
-
{ () x, factorial "x!" }
|
|
135
|
-
|
|
136
|
-
## Codes (_types_)
|
|
137
|
-
|
|
138
|
-
_Codes_ (prefixed by `$`) can be defined by taged union and product. E.g.:
|
|
139
|
-
|
|
140
|
-
$nat = <nat 1, {} 0>;
|
|
141
|
-
$pair = {nat x, nat y};
|
|
142
|
-
|
|
143
|
-
suc = {$nat 1};
|
|
144
|
-
add = $pair <{.x.1 x, .y suc y} add, .y>;
|
|
145
|
-
|
|
146
|
-
### Basic extension codes
|
|
147
|
-
|
|
148
|
-
Since _basic extension_ introduces integers, booleans, and strings, there are three
|
|
149
|
-
predefined types: `int`, `bool`, and `string`. A vector product code can also be defined
|
|
150
|
-
by `[ codeExp ]`. All members of the vector are the same code. E.g.,
|
|
151
|
-
|
|
152
|
-
$intVector = [ int ];
|
|
153
|
-
$boolVector = [ bool ];
|
|
154
|
-
$tree = [ tree ];
|
|
155
|
-
|
|
156
|
-
emptyList? = $intVector $[ string ]; -- as only an empty vector can be a vector
|
|
157
|
-
-- of integers and a vector of strings
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|