@fraczak/k 2.0.0 → 2.0.2
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 +259 -51
- package/codes.mjs +0 -1
- package/index.mjs +0 -3
- package/k.mjs +29 -42
- package/package.json +1 -1
- package/run.mjs +5 -0
- package/INTRO.md +0 -160
package/README.md
CHANGED
|
@@ -10,31 +10,206 @@ 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
|
+
Those values, i.e., `strings`, `integers`, `booleans`, and `null`, admit the
|
|
98
|
+
projection to the canonicat string representation of the value, e.g.:
|
|
99
|
+
|
|
100
|
+
.2 :
|
|
101
|
+
2 --> {}
|
|
102
|
+
"2" --> {}
|
|
103
|
+
4 ... undefined
|
|
104
|
+
true ... undefined
|
|
105
|
+
|
|
106
|
+
.true :
|
|
107
|
+
true --> {}
|
|
108
|
+
"true" --> {}
|
|
109
|
+
4 ... undefined
|
|
110
|
+
"toto" ... undefined
|
|
111
|
+
|
|
112
|
+
.null :
|
|
113
|
+
null --> {}
|
|
114
|
+
"null" --> {}
|
|
115
|
+
4 ... undefined
|
|
116
|
+
false ... undefined
|
|
117
|
+
|
|
118
|
+
#### Vector product
|
|
119
|
+
|
|
120
|
+
Vector product can be seen as an abbreviation for product whose field
|
|
121
|
+
names are integers starting from zero. E.g., `{.toto 0, .titi 1, 123 2}`
|
|
122
|
+
can be written as `[.toto, .titi, 123]`.
|
|
123
|
+
|
|
124
|
+
[.toto, .titi, 12] :
|
|
125
|
+
{"toto": 5, "titi": 10 } --> [5, 10, 12]
|
|
126
|
+
|
|
127
|
+
.1 :
|
|
128
|
+
["A","B","C"] --> "B"
|
|
129
|
+
["a"] ... undefined
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Pragmatic extensions, aka "standard library"
|
|
134
|
+
|
|
135
|
+
- `GT` -- identity for lists of decreasing elements; undefined otherwise
|
|
136
|
+
|
|
137
|
+
GT:
|
|
138
|
+
[4,3] --> [4,3]
|
|
139
|
+
[3,4] ... undefined
|
|
140
|
+
[] --> []
|
|
141
|
+
[4,3,0] --> [4,3,0]
|
|
142
|
+
|
|
143
|
+
- `EQ` -- identity for lists of equal elements; undefined otherwise
|
|
144
|
+
|
|
145
|
+
EQ:
|
|
146
|
+
[4,4] --> [4,4]
|
|
147
|
+
[4,5] ... undefined
|
|
148
|
+
[4,4,4] --> [4,4,4]
|
|
149
|
+
[] --> []
|
|
150
|
+
|
|
151
|
+
- `PLUS` and `TIMES` -- sum and product of lists of numbers
|
|
152
|
+
|
|
153
|
+
{PLUS plus, TIMES times} :
|
|
154
|
+
[1,2] --> {"plus":3,"times":2}
|
|
155
|
+
[2,2,2] --> {"plus":6,"times":8}
|
|
156
|
+
[] --> {"plus":0,"times":1}
|
|
157
|
+
|
|
158
|
+
- `CONCAT` -- concatenation of lists of strings
|
|
159
|
+
|
|
160
|
+
CONCAT:
|
|
161
|
+
["a","bc","d"] --> "abcd"
|
|
162
|
+
["a","bc","d"] --> "abcd"
|
|
163
|
+
|
|
164
|
+
- `toJSON` and `fromJSON` -- conversion to and from JSON strings
|
|
165
|
+
|
|
166
|
+
toJSON:
|
|
167
|
+
{"a": 12} --> "{\"a\":12}"
|
|
168
|
+
|
|
169
|
+
fromJSON:
|
|
170
|
+
"{\"a\":12}" --> {"a":12}
|
|
171
|
+
"2.12" --> 2.12
|
|
172
|
+
"[1,2.11,0.3e-32]" --> [1,2.11,3e-33]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
- other predefined parial functions are: `DIV`, `FDIV`, `CONS`, `SNOC`, `toDateMsec`,
|
|
176
|
+
`toDateStr`, and `_log!`.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### Function definitions
|
|
181
|
+
|
|
182
|
+
dec = [(),-1] PLUS;
|
|
183
|
+
max = <SNOC [.0, .1 max] <GT.0, .1>, .0> ;
|
|
184
|
+
factorial = < [(),0] GT .0 [dec factorial, ()] TIMES, 1 >;
|
|
185
|
+
|
|
186
|
+
### Codes (_types_)
|
|
187
|
+
|
|
188
|
+
_Codes_ (prefixed by `$`) can be defined by taged union and product. E.g.:
|
|
189
|
+
|
|
190
|
+
$nat = <nat 1, {} 0>;
|
|
191
|
+
$pair = {nat x, nat y};
|
|
192
|
+
|
|
193
|
+
suc = {$nat 1};
|
|
194
|
+
add = $pair <{.x.1 x, .y suc y} add, .y>;
|
|
195
|
+
|
|
196
|
+
#### Basic extension codes
|
|
197
|
+
|
|
198
|
+
Since _basic extension_ introduces integers, booleans, and strings, there are three
|
|
199
|
+
predefined types: `int`, `bool`, and `string`. A vector product code can also be defined
|
|
200
|
+
by `[ codeExp ]`. All members of the vector are the same code. E.g.,
|
|
201
|
+
|
|
202
|
+
$intVector = [ int ];
|
|
203
|
+
$boolVector = [ bool ];
|
|
204
|
+
$tree = [ tree ];
|
|
205
|
+
|
|
206
|
+
emptyList? = $intVector $[ string ]; -- as only an empty vector can be a vector
|
|
207
|
+
-- of integers and a vector of strings
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Examples
|
|
211
|
+
|
|
212
|
+
1. projection:
|
|
38
213
|
|
|
39
214
|
.x
|
|
40
215
|
."field name"
|
|
@@ -43,7 +218,7 @@ other `k`-expressions in three ways:
|
|
|
43
218
|
The function is defined only if its argument is a structure with
|
|
44
219
|
the field (or a vector with the index).
|
|
45
220
|
|
|
46
|
-
|
|
221
|
+
2. _constants_, literals for Strings, Booleans, and
|
|
47
222
|
Integers. Examples:
|
|
48
223
|
|
|
49
224
|
"a string"
|
|
@@ -52,14 +227,12 @@ the field (or a vector with the index).
|
|
|
52
227
|
false
|
|
53
228
|
null
|
|
54
229
|
|
|
55
|
-
|
|
56
|
-
`TIMES`, `DIV`, `CONCAT`, `toJSON`, `fromJSON`, `CONS`,
|
|
57
|
-
`SNOC`, `toDateMsec`, `toDateStr`, and `_log!`. For example:
|
|
230
|
+
3. "built-in" functions:
|
|
58
231
|
|
|
59
|
-
[1, 2, 3] PLUS
|
|
60
|
-
[4, 4] TIMES toJSON
|
|
61
|
-
[3, 2] GT
|
|
62
|
-
[3, 4] GT ...
|
|
232
|
+
[1, 2, 3] PLUS -- integer constant function 6
|
|
233
|
+
[4, 4] TIMES toJSON -- string constant function "16"
|
|
234
|
+
[3, 2] GT -- vector constant function [3, 2]
|
|
235
|
+
[3, 4] GT -- ... undefined
|
|
63
236
|
|
|
64
237
|
A more interesting example could be:
|
|
65
238
|
|
|
@@ -69,7 +242,7 @@ the field (or a vector with the index).
|
|
|
69
242
|
|
|
70
243
|
[3,8] < GT .0, .1 > --> 8
|
|
71
244
|
|
|
72
|
-
|
|
245
|
+
### User defined functions
|
|
73
246
|
|
|
74
247
|
`k`-expression can be prefixed by function definitions. E.g.:
|
|
75
248
|
|
|
@@ -84,22 +257,22 @@ the field (or a vector with the index).
|
|
|
84
257
|
Another example could be finding the biggest (max) value in a vector:
|
|
85
258
|
|
|
86
259
|
max = <
|
|
87
|
-
SNOC
|
|
88
|
-
[.0, .1 max]
|
|
89
|
-
<GT .0, .1
|
|
90
|
-
|
|
91
|
-
.0
|
|
260
|
+
SNOC # [x0, x1, x2, ...] --> [x0, [x1, x2, ...]]
|
|
261
|
+
[.0, .1 max] # [x0, [x1, x2, ...]] --> [x0, max(x1,x2,...)], i.e., recursive call
|
|
262
|
+
<GT .0, .1> # if x0 > max(x1,x2,...) then x0 else max(x1,x2,...)
|
|
263
|
+
, # when SNOC is not defined, i.e., if the input vector has one element:
|
|
264
|
+
.0 # [x0] --> x0
|
|
92
265
|
>;
|
|
93
266
|
max
|
|
94
267
|
|
|
95
|
-
|
|
268
|
+
### Value encodings (_codes_)
|
|
96
269
|
|
|
97
270
|
There are three predefined value encodings: `int`, `string`, and
|
|
98
271
|
`bool`. The language supports `code`-expressions:
|
|
99
272
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
273
|
+
- product, e.g., `{int x, int y, bool flag}`
|
|
274
|
+
- disjoint union, e.g., `<{} true, {} false>`
|
|
275
|
+
- vector, e.g., `[ int ]` (all elements of the vector use the same
|
|
103
276
|
encoding)
|
|
104
277
|
|
|
105
278
|
One can define recursive codes. E.g.:
|
|
@@ -121,34 +294,38 @@ within `k`-expression is again prefixed by `$`.
|
|
|
121
294
|
> $ int;
|
|
122
295
|
height
|
|
123
296
|
|
|
124
|
-
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## `k` (a command-line JSON processor using `k` syntax)
|
|
125
300
|
|
|
126
|
-
There is a wrapper, `./node_modules/.bin/k`
|
|
301
|
+
There is a wrapper, `k` (`./node_modules/.bin/k`), which makes it easy to
|
|
127
302
|
run the language from command line.
|
|
128
303
|
|
|
129
|
-
>
|
|
130
|
-
|
|
304
|
+
> k
|
|
305
|
+
... errors ...
|
|
131
306
|
Usage: ./node_modules/.bin/k ( k-expr | -k k-file) [ -1 ] [ json-file ]
|
|
132
307
|
E.g., cat '{"a": 10}' | ./node_modules/.bin/k '[(),()]'
|
|
133
308
|
|
|
134
|
-
|
|
309
|
+
For example:
|
|
135
310
|
|
|
136
311
|
1. One `k`-expression with one `json`-object:
|
|
137
312
|
|
|
138
|
-
> echo '{"x": 12, "y": 13}' |
|
|
313
|
+
> echo '{"x": 12, "y": 13}' | k '{ <.x, "no x"> x, () input}'
|
|
139
314
|
{"x":12,"input":{"x":12,"y":13}}
|
|
140
315
|
|
|
141
316
|
2. By providing only `k`-expression, the script will compile the
|
|
142
317
|
`k`-expression and apply the generated function to the `stdin`,
|
|
143
318
|
line by line:
|
|
144
319
|
|
|
145
|
-
>
|
|
320
|
+
> k '<["x=",.x," & y=",.y],["only x=",.x],["only y=",.y],["no x nor y"]>{CONCAT "x&y"}'
|
|
146
321
|
|
|
147
322
|
{"y": 123, "x": 432,"others": "..."} --> {"x&y":"x=432 & y=123"}
|
|
148
323
|
{"x": 987} --> {"x&y":"only x=987"}
|
|
149
324
|
{"z": 123} --> {"x&y":"no x nor y"}
|
|
150
325
|
^D - to interrupt
|
|
151
326
|
|
|
327
|
+
If the input is a multiline json object, we need to add `-1` to the command-line options.
|
|
328
|
+
|
|
152
329
|
3. If the `k`-expression is long, it can be put in a file, e.g.:
|
|
153
330
|
|
|
154
331
|
> cat test.k
|
|
@@ -168,11 +345,11 @@ run the language from command line.
|
|
|
168
345
|
|
|
169
346
|
We can use it by:
|
|
170
347
|
|
|
171
|
-
>
|
|
348
|
+
> k -k test.k
|
|
172
349
|
|
|
173
350
|
If we want to read `json` objects from a file, e.g., `my-objects.json`, we do
|
|
174
351
|
|
|
175
|
-
>
|
|
352
|
+
> k -k test.k my-objects.json
|
|
176
353
|
{"x&y":"x=432 & y=123"}
|
|
177
354
|
{"x&y":"only x=987"}
|
|
178
355
|
{"x&y":"no x nor y"}
|
|
@@ -187,9 +364,38 @@ run the language from command line.
|
|
|
187
364
|
{"z": 123}
|
|
188
365
|
####################################################
|
|
189
366
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
### Short comparaison with `jq` tutorial examples: <https://stedolan.github.io/jq/tutorial/>
|
|
370
|
+
|
|
371
|
+
1.
|
|
372
|
+
curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | jq '.'
|
|
373
|
+
curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | k '()' -1
|
|
374
|
+
2.
|
|
375
|
+
curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | jq '.[0]'
|
|
376
|
+
curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | k '.0' -1
|
|
377
|
+
3.
|
|
378
|
+
jq '.[0] | {message: .commit.message, name: .commit.committer.name}'
|
|
379
|
+
k '.0 {.commit.message message, .commit.committer.name name}' -1
|
|
380
|
+
4. note: `k`-expression defines a partial function yielding a single json object, i.e., as far as `k` is concerned,
|
|
381
|
+
examples 4 and 5 of the `jq` tutorial are equivalent.
|
|
382
|
+
5.
|
|
383
|
+
jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'
|
|
384
|
+
k 'f = .commit {.message message, .committer.name name}; %\
|
|
385
|
+
map_f = <SNOC [.0 f,.1 map_f] CONS, [.0 f], []>; %\
|
|
386
|
+
map_f' -1
|
|
387
|
+
6.
|
|
388
|
+
jq '[.[] | {message: .commit.message, name: .commit.committer.name, parents: [.parents[].html_url]}]'
|
|
389
|
+
k 'map_html_url = <SNOC [.0 .html_url, .1 map_html_url] CONS, [.0 .html_url], []>; %\
|
|
390
|
+
f = {.commit.message message, .commit.committer.name name, .parents map_html_url parents}; %\
|
|
391
|
+
map_f = <SNOC [.0 f, .1 map_f] CONS, [.0 f] , []>; %\
|
|
392
|
+
map_f' -1
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## k-REPL (Read-Evaluate-Print Loop)
|
|
397
|
+
|
|
398
|
+
Also there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`), which acts like a toy
|
|
193
399
|
shell for the language. E.g.:
|
|
194
400
|
|
|
195
401
|
> ./node_modules/.bin/k-repl
|
|
@@ -202,11 +408,13 @@ shell for the language. E.g.:
|
|
|
202
408
|
inc inc inc inc
|
|
203
409
|
=> 7
|
|
204
410
|
|
|
205
|
-
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Using `k` from `javascript`
|
|
206
414
|
|
|
207
|
-
|
|
415
|
+
import k from "@fraczak/k";
|
|
208
416
|
|
|
209
|
-
k_expression = '()';
|
|
417
|
+
let k_expression = '()';
|
|
210
418
|
k.run(k_expression,"ANYTHING...");
|
|
211
419
|
// RETURNS: "ANYTHING..."
|
|
212
420
|
|
|
@@ -249,7 +457,7 @@ shell for the language. E.g.:
|
|
|
249
457
|
k_fn({"age":2,"name":"Mark"});
|
|
250
458
|
// RETURNS: {"nom":"Mark","age":"enfant"}
|
|
251
459
|
|
|
252
|
-
|
|
460
|
+
k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
|
|
253
461
|
|
|
254
462
|
k_fn(1);
|
|
255
463
|
// RETURNS: 1
|
|
@@ -263,7 +471,7 @@ shell for the language. E.g.:
|
|
|
263
471
|
k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
|
|
264
472
|
// RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
|
|
265
473
|
|
|
266
|
-
|
|
474
|
+
k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
|
|
267
475
|
|
|
268
476
|
k_fn({"None":"None"});
|
|
269
477
|
// 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,11 @@ function run(exp, value) {
|
|
|
101
101
|
}
|
|
102
102
|
return builtin[exp.ref](value);
|
|
103
103
|
case "dot":
|
|
104
|
+
// a hack to allow something like 'null . null' or '0 . 0' to work by returning unit
|
|
105
|
+
if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
106
|
+
if (`${value}` === `${exp.dot}`) return {};
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
104
109
|
return value[exp.dot];
|
|
105
110
|
case "comp":
|
|
106
111
|
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
|
-
|