@fraczak/k 1.1.0 → 1.1.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # k-language
2
2
 
3
+ npm install @fraczak/k
4
+
3
5
  Another JSON transformation notation. A `k`-expression
4
6
  (script) defines a __partial function__, i.e., a function which, in
5
7
  general, is not defined for every input. `k`-expressions can be
@@ -48,18 +50,18 @@ Elementary partial functions are:
48
50
  3. There is a number of built-in functions: `GT`, `EQ`, `PLUS`,
49
51
  `TIMES`, `MINUS`, `DIV`, `CONCAT`, `toJSON`, `fromJSON`, `CONS`, `SNOC`. For example
50
52
 
51
- [1, 2, 3] PLUS --> integer constant 6
52
- [4, 4] TIMES toJSON --> string constant "16"
53
- [3, 2] GT --> vector constant [3, 2]
53
+ [1, 2, 3] PLUS --> integer constant function 6
54
+ [4, 4] TIMES toJSON --> string constant function "16"
55
+ [3, 2] GT --> vector constant function [3, 2]
54
56
  [3, 4] GT --> is not defined!
55
57
 
56
58
  A more interesting example could be:
57
59
 
58
- < [.0, .1] GT, [.1, .0] GT > .0
60
+ < GT .0, .1>
59
61
 
60
- which selects the bigger of two first integers of a vector argument, i.e.,
62
+ which selects the maximum element in two element vector, i.e.,
61
63
 
62
- [3,8] < [.0, .1] GT, [.1, .0] GT > .0 --> 8
64
+ [3,8] < GT .0, .1 > --> 8
63
65
 
64
66
  ## User defined functions
65
67
 
@@ -70,18 +72,33 @@ Elementary partial functions are:
70
72
  [1,()] GT 1,
71
73
  [dec factorial, ()] TIMES
72
74
  >;
73
- { x: (), "x!": factorial }
75
+ { () x, factorial "x!" }
76
+
77
+ Another example could be finding the biggest (max) value in a vector:
74
78
 
75
- ## Value encodings (also called _types_ or _codes_)
79
+ max = <
80
+ SNOC -- [x0, x1, x2, ...] -> [x0, [x1, x2, ...]]
81
+ [.0, .1 max] -- [x0, [x1, x2, ...]] -> [x0, max(x1,x2,...)], i.e., recursive call
82
+ <GT.0,.1>, -- if x0 > max(x1,x2,...) then x0 else max(x1,x2,...)
83
+ -- when SNOC is not defined, e.g. the argument is a singleton vector [x0]
84
+ .0 -- [x0] -> x0,
85
+ >;
86
+ max
87
+
88
+ ## Value encodings (also called _codes_)
76
89
 
77
90
  There are three predefined value encodings: `int`, `string`, and `bool`. The language
78
91
  supports `code`-expressions:
79
92
 
80
- * product, e.g., `{x: int, y: int, flag: bool}`
81
- * disjoint union, e.g., `<x: string, y: bool>`
93
+ * product, e.g., `{int x, int y, bool flag}` (in JSON-like syntax `{x: int, y: int, flag: bool}`)
94
+ * disjoint union, e.g., `<{} true, {} false>` (in JSON-like syntax `<true: {}, false: {}>`)
82
95
  * vector, e.g., `[ int ]` (all elements of the vector use the same encoding)
83
96
 
84
- One can define recursive codes. E.g.,
97
+ One can define recursive codes. E.g.:
98
+
99
+ $ tree = <string leaf, {tree left, tree right} tree>;
100
+
101
+ or, the same in JSON-like notation:
85
102
 
86
103
  $ tree = <leaf: string, tree: {left: tree, right: tree}>;
87
104
 
@@ -91,11 +108,12 @@ The above example defines new code called _tree_.
91
108
  The code can be then used in a `k`-expression as a filter. A `code`-expression
92
109
  within `k`-expression is again prefixed by `$`.
93
110
 
94
- $tree = <leaf: string, tree: {left: tree, right: tree}>;
111
+ $ tree = <string leaf, {tree left, tree right} tree>;
95
112
  inc = [(),1] PLUS;
113
+ max = <GT .0, .1>;
96
114
  height = $ tree <
97
115
  .leaf 0,
98
- .tree [.left height, .right height] <GT .0, .1> inc
116
+ .tree [.left height, .right height] max inc
99
117
  > $ int;
100
118
  height
101
119
 
@@ -107,7 +125,7 @@ run the language from command line.
107
125
 
108
126
  $ ./node_modules/.bin/k
109
127
  ... errors ...
110
- Usage: ./node_modules/.bin/k ( k-expr | -k k-file) [ json-file ]
128
+ Usage: ./node_modules/.bin/k ( k-expr | -k k-file) [ -1 ] [ json-file ]
111
129
  E.g., cat '{"a": 10}' | ./node_modules/.bin/k '[(),()]'
112
130
 
113
131
  ### Examples
@@ -120,7 +138,7 @@ run the language from command line.
120
138
  2. By providing only `k`-expression, the script will compile the
121
139
  `k`-expression and apply the generated function to the `stdin`, line by line:
122
140
 
123
- $ ./k.coffee '<["x=",.x," & y=",.y],["only x=",.x],["only y=",.y],["no x nor y"]>{CONCAT "x&y"}'
141
+ $ ./node_modules/.bin/k '<["x=",.x," & y=",.y],["only x=",.x],["only y=",.y],["no x nor y"]>{CONCAT "x&y"}'
124
142
 
125
143
  {"y": 123, "x": 432,"others": "..."} --> {"x&y":"x=432 & y=123"}
126
144
  {"x":987} --> {"x&y":"only x=987"}
@@ -146,11 +164,11 @@ run the language from command line.
146
164
 
147
165
  We can use it by:
148
166
 
149
- $ ./k.coffee -k test.k
167
+ $ ./node_modules/.bin/k -k test.k
150
168
 
151
169
  If we want to read `json` objects from a file, e.g., `my-objects.json`, we do
152
170
 
153
- $ ./k.coffee -k test.k my-objects.json
171
+ $ ./node_modules/.bin/k -k test.k my-objects.json
154
172
  {"x&y":"x=432 & y=123"}
155
173
  {"x&y":"onlyx=987"}
156
174
  {"x&y":"no x nor y"}
@@ -166,52 +184,62 @@ run the language from command line.
166
184
  ####################################################
167
185
 
168
186
  # Using from `javascript`
169
-
170
- var k = require("k");
187
+
188
+ var k = require("k");
189
+
190
+ k_expression = '()';
191
+ k.run(k_expression,"ANYTHING...");
192
+ // RETURNS: "ANYTHING..."
193
+
194
+ k_expression = '{"ala" name, 23 age}';
195
+ k.run(k_expression,"ANYTHING...");
196
+ // RETURNS: {"name":"ala","age":23}
197
+
198
+ k_expression = '[.year, .age]';
199
+ k.run(k_expression,{"year":2002,"age":19});
200
+ // RETURNS: [2002,19]
171
201
 
172
- var k_expression;
202
+ k_expression = '[(), ()]';
203
+ k.run(k_expression,"duplicate me");
204
+ // RETURNS: ["duplicate me","duplicate me"]
173
205
 
174
- k_expression = '()';
175
- k.run(t_expression,"ANYTHING...");
176
- // RETURNS: "ANYTHING..."
206
+ k_expression = '[[[()]]]';
207
+ k.run(k_expression,"nesting");
208
+ // RETURNS: [[["nesting"]]]
177
209
 
178
- k_expression = '{"ala" name, 23 age}';
179
- k.run(k_expression,"ANYTHING...");
180
- // RETURNS: {"name":"ala","age":23}
210
+ k_expression = '[[()]] {() nested, .0.0 val}';
211
+ k.run(k_expression,"nesting and accessing");
212
+ // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
181
213
 
182
- k_expression = '[.year, .age]';
183
- k.run(k_expression,{"year":2002,"age":19});
184
- // RETURNS: [2002,19]
214
+ k_expression = '0000';
215
+ k.run(k_expression,{"test":"parse integer"});
216
+ // RETURNS: 0
185
217
 
186
- k_expression = '[(), ()]';
187
- k.run(k_expression,"duplicate me");
188
- // RETURNS: ["duplicate me","duplicate me"]
218
+ k_expression = '[.y,.x] PLUS';
219
+ k.run(k_expression,{"x":3,"y":4});
220
+ // RETURNS: 7
189
221
 
190
- k_expression = '[[[()]]]';
191
- k.run(k_expression,"nesting");
192
- // RETURNS: [[["nesting"]]]
222
+ var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
193
223
 
194
- k_expression = '[[()]] {() nested, .0.0 val}';
195
- k.run(k_expression,"nesting and accessing")
196
- // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
224
+ k_fn({"age":23,"name":"Emily"});
225
+ // RETURNS: {"nom":"Emily","age":23}
197
226
 
198
- k_expression = '0000';
199
- k.run(k_expression,{"test":"parse integer"});
200
- // RETURNS: 0
227
+ k_fn({"age":16,"name":"Katrina"});
228
+ // RETURNS: {"nom":"Katrina","age":"ado"}
201
229
 
202
- k_expression = '[.y,.x] PLUS';
203
- k.run(k_expression,{"x":3,"y":4});
204
- // RETURNS: 7
230
+ k_fn({"age":2,"name":"Mark"});
231
+ // RETURNS: {"nom":"Mark","age":"enfant"}
205
232
 
206
- k_expression = '{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}';
233
+ var k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
207
234
 
208
- var k_fn = k.compile(k_expression);
235
+ k_fn(1);
236
+ // RETURNS: 1
209
237
 
210
- k_fn({"age":23,"name":"Emily"});
211
- // RETURNS: {"nom":"Emily","age":23}
238
+ k_fn({"i":1});
239
+ // RETURNS: {"i":1}
212
240
 
213
- k_fn({"age":16,"name":"Katrina"});
214
- // RETURNS: {"nom":"Katrina","age":"ado"}
241
+ k_fn([{"i":2},{"i":3},{"t":[]}]);
242
+ // RETURNS: undefined
215
243
 
216
- k_fn({"age":2,"name":"Mark"})
217
- // RETURNS: {"nom":"Mark","age":"enfant"}
244
+ k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
245
+ // RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
package/bnat.k CHANGED
@@ -71,7 +71,7 @@ _reverse_bnat =
71
71
  _add2wc =
72
72
  -- least significant bit first addition with carry
73
73
  --- :{ :bnat x, :bnat y, :[0,1] c}
74
- -- _log_
74
+ -- _log!
75
75
  <
76
76
  {.x."0" x, .y."0" y, .c."0" c} {.x x, .y y, _ _0 c} _add2wc _0,
77
77
  {.x."0" x, .y."0" y, .c."1" c} {.x x, .y y, _ _0 c} _add2wc _1,
package/code.k CHANGED
@@ -1,8 +1,9 @@
1
1
  inc = [(),1] PLUS;
2
- $tree = <leaf: string, tree: {left: tree, right: tree}>;
2
+ max = <GT .0, .1>; -- max = < SNOC [.0, .1 max] <GT .0, .1>, .0 >;
3
+ $tree = < string leaf, {tree left, tree right} tree>;
3
4
  height = $tree <
4
- .leaf 0,
5
- .tree [.left height, .right height] <GT .0, .1> inc
5
+ .leaf 0,
6
+ .tree [.left height, .right height] max inc
6
7
  > $int;
7
8
 
8
9
  <$tree,
@@ -15,4 +16,4 @@ height = $tree <
15
16
  }
16
17
  }
17
18
  }
18
- > _log_ height
19
+ > _log! height
package/codes.coffee ADDED
@@ -0,0 +1,109 @@
1
+
2
+ are_different = (classes, representatives, name1, name2, codes) ->
3
+ return false if name1 is name2
4
+ code1 = codes[name1]
5
+ code2 = codes[name2]
6
+ # console.log {name1, code1, name2, code2}
7
+ return true unless code1.code is code2.code
8
+ switch code1.code
9
+ when "union", "product"
10
+ [fields1,fields2] = [code1, code2]. map (code) ->
11
+ do (arg = code[code.code]) ->
12
+ Object.keys(arg).reduce (fields, label) ->
13
+ fields[label] = representatives[arg[label]] ? arg[label]
14
+ , {}
15
+ return true unless Object.keys(fields1).length is Object.keys(fields2).length
16
+ for field,rep of fields1
17
+ return true unless fields2[field] is rep
18
+ when "vector"
19
+ [arg1,arg2] = [code1, code2]. map ({vector}) ->
20
+ representatives[vector] ? vector
21
+ return true unless arg1 is arg2
22
+ false
23
+
24
+ minimize = (codes) ->
25
+ names = Object.keys codes
26
+ classes = {}
27
+ classes["{}"] = names
28
+ representatives = names.reduce (representatives, name) ->
29
+ representatives[name] = "{}"
30
+ representatives
31
+ , {}
32
+ changed = true
33
+ while changed
34
+ changed = false
35
+ for name1, eq_names of classes
36
+ code = codes[name1]
37
+ [eq_names, dif_names] = eq_names.reduce ([eq_names, dif_names], name2) ->
38
+ if are_different(classes, representatives, name1, name2, codes)
39
+ dif_names.push name2
40
+ else
41
+ eq_names.push name2
42
+ [eq_names,dif_names]
43
+ , [[],[]]
44
+ classes[name1] = eq_names
45
+ do (new_rep = dif_names[0]) ->
46
+ if new_rep?
47
+ changed = true
48
+ classes[new_rep] = dif_names
49
+ for x in dif_names
50
+ representatives[x] = new_rep
51
+ {classes,representatives}
52
+
53
+ compareAs = (fn) ->
54
+ (a,b) ->
55
+ [a,b] = [a,b].map fn
56
+ return -1 if a < b
57
+ return 1 if a > b
58
+ 0
59
+
60
+ pretty_labels = (label_ref_map, codes, representatives) ->
61
+ labels = Object.keys(label_ref_map)
62
+ .sort compareAs (x) -> x
63
+ .map (label) ->
64
+ "#{pretty {code:"ref",ref:label_ref_map[label]}, codes, representatives} #{label}"
65
+ .join ", "
66
+
67
+ pretty = (codeExp, codes, representatives) ->
68
+ switch codeExp.code
69
+ when "ref"
70
+ name = representatives[codeExp.ref] ? codeExp.ref
71
+ if name.startsWith ":"
72
+ pretty codes[name], codes, representatives
73
+ else
74
+ name
75
+ when "product"
76
+ "{#{pretty_labels codeExp.product, codes, representatives}}"
77
+ when "union"
78
+ "<#{pretty_labels codeExp.union, codes, representatives}>"
79
+ when "vector"
80
+ "[#{representatives[codeExp.ref] ? codeExp.ref}]"
81
+ else
82
+ ":error"
83
+
84
+ normalize = (label_ref_map, representatives) ->
85
+ # console.log {label_ref_map, representatives}
86
+ Object.keys(label_ref_map).reduce (result, label) ->
87
+ result[label] = do (name = label_ref_map[label]) ->
88
+ representatives[name] ? name
89
+ result
90
+ , {}
91
+
92
+ normalizeAll = (codes,representatives) ->
93
+ names = Object.keys codes
94
+ normalized = names.reduce (normalized, name) ->
95
+ if name is representatives[name]
96
+ do (code = codes[name]) ->
97
+ switch code.code
98
+ when "union", "product"
99
+ normalized[name] = { code: code.code }
100
+ normalized[name][code.code] = normalize code[code.code], representatives
101
+ when "vector"
102
+ normalized[name] = {code:"vector", vector: representatives[code.vector]}
103
+
104
+ normalized
105
+ , {}
106
+
107
+ module.exports = {minimize,pretty,normalize,normalizeAll}
108
+
109
+
package/codes.js ADDED
@@ -0,0 +1,176 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ (function() {
3
+ var are_different, compareAs, minimize, normalize, normalizeAll, pretty, pretty_labels;
4
+
5
+ are_different = function(classes, representatives, name1, name2, codes) {
6
+ var arg1, arg2, code1, code2, field, fields1, fields2, rep;
7
+ if (name1 === name2) {
8
+ return false;
9
+ }
10
+ code1 = codes[name1];
11
+ code2 = codes[name2];
12
+ if (code1.code !== code2.code) {
13
+ // console.log {name1, code1, name2, code2}
14
+ return true;
15
+ }
16
+ switch (code1.code) {
17
+ case "union":
18
+ case "product":
19
+ [fields1, fields2] = [code1, code2].map(function(code) {
20
+ return (function(arg) {
21
+ return Object.keys(arg).reduce(function(fields, label) {
22
+ var ref;
23
+ return fields[label] = (ref = representatives[arg[label]]) != null ? ref : arg[label];
24
+ }, {});
25
+ })(code[code.code]);
26
+ });
27
+ if (Object.keys(fields1).length !== Object.keys(fields2).length) {
28
+ return true;
29
+ }
30
+ for (field in fields1) {
31
+ rep = fields1[field];
32
+ if (fields2[field] !== rep) {
33
+ return true;
34
+ }
35
+ }
36
+ break;
37
+ case "vector":
38
+ [arg1, arg2] = [code1, code2].map(function({vector}) {
39
+ var ref;
40
+ return (ref = representatives[vector]) != null ? ref : vector;
41
+ });
42
+ if (arg1 !== arg2) {
43
+ return true;
44
+ }
45
+ }
46
+ return false;
47
+ };
48
+
49
+ minimize = function(codes) {
50
+ var changed, classes, code, dif_names, eq_names, name1, names, representatives;
51
+ names = Object.keys(codes);
52
+ classes = {};
53
+ classes["{}"] = names;
54
+ representatives = names.reduce(function(representatives, name) {
55
+ representatives[name] = "{}";
56
+ return representatives;
57
+ }, {});
58
+ changed = true;
59
+ while (changed) {
60
+ changed = false;
61
+ for (name1 in classes) {
62
+ eq_names = classes[name1];
63
+ code = codes[name1];
64
+ [eq_names, dif_names] = eq_names.reduce(function([eq_names, dif_names], name2) {
65
+ if (are_different(classes, representatives, name1, name2, codes)) {
66
+ dif_names.push(name2);
67
+ } else {
68
+ eq_names.push(name2);
69
+ }
70
+ return [eq_names, dif_names];
71
+ }, [[], []]);
72
+ classes[name1] = eq_names;
73
+ (function(new_rep) {
74
+ var i, len, results, x;
75
+ if (new_rep != null) {
76
+ changed = true;
77
+ classes[new_rep] = dif_names;
78
+ results = [];
79
+ for (i = 0, len = dif_names.length; i < len; i++) {
80
+ x = dif_names[i];
81
+ results.push(representatives[x] = new_rep);
82
+ }
83
+ return results;
84
+ }
85
+ })(dif_names[0]);
86
+ }
87
+ }
88
+ return {classes, representatives};
89
+ };
90
+
91
+ compareAs = function(fn) {
92
+ return function(a, b) {
93
+ [a, b] = [a, b].map(fn);
94
+ if (a < b) {
95
+ return -1;
96
+ }
97
+ if (a > b) {
98
+ return 1;
99
+ }
100
+ return 0;
101
+ };
102
+ };
103
+
104
+ pretty_labels = function(label_ref_map, codes, representatives) {
105
+ var labels;
106
+ return labels = Object.keys(label_ref_map).sort(compareAs(function(x) {
107
+ return x;
108
+ })).map(function(label) {
109
+ return `${pretty({
110
+ code: "ref",
111
+ ref: label_ref_map[label]
112
+ }, codes, representatives)} ${label}`;
113
+ }).join(", ");
114
+ };
115
+
116
+ pretty = function(codeExp, codes, representatives) {
117
+ var name, ref, ref1;
118
+ switch (codeExp.code) {
119
+ case "ref":
120
+ name = (ref = representatives[codeExp.ref]) != null ? ref : codeExp.ref;
121
+ if (name.startsWith(":")) {
122
+ return pretty(codes[name], codes, representatives);
123
+ } else {
124
+ return name;
125
+ }
126
+ break;
127
+ case "product":
128
+ return `{${pretty_labels(codeExp.product, codes, representatives)}}`;
129
+ case "union":
130
+ return `<${pretty_labels(codeExp.union, codes, representatives)}>`;
131
+ case "vector":
132
+ return `[${(ref1 = representatives[codeExp.ref]) != null ? ref1 : codeExp.ref}]`;
133
+ default:
134
+ return ":error";
135
+ }
136
+ };
137
+
138
+ normalize = function(label_ref_map, representatives) {
139
+ // console.log {label_ref_map, representatives}
140
+ return Object.keys(label_ref_map).reduce(function(result, label) {
141
+ result[label] = (function(name) {
142
+ var ref;
143
+ return (ref = representatives[name]) != null ? ref : name;
144
+ })(label_ref_map[label]);
145
+ return result;
146
+ }, {});
147
+ };
148
+
149
+ normalizeAll = function(codes, representatives) {
150
+ var names, normalized;
151
+ names = Object.keys(codes);
152
+ return normalized = names.reduce(function(normalized, name) {
153
+ if (name === representatives[name]) {
154
+ (function(code) {
155
+ switch (code.code) {
156
+ case "union":
157
+ case "product":
158
+ normalized[name] = {
159
+ code: code.code
160
+ };
161
+ return normalized[name][code.code] = normalize(code[code.code], representatives);
162
+ case "vector":
163
+ return normalized[name] = {
164
+ code: "vector",
165
+ vector: representatives[code.vector]
166
+ };
167
+ }
168
+ })(codes[name]);
169
+ }
170
+ return normalized;
171
+ }, {});
172
+ };
173
+
174
+ module.exports = {minimize, pretty, normalize, normalizeAll};
175
+
176
+ }).call(this);
package/fib.k CHANGED
@@ -1,4 +1,4 @@
1
- fib_aux =
1
+ fib_aux = _log!
2
2
  <
3
3
  { [.n, 1] GT [.0,-1] PLUS n,
4
4
  ."[fib(n),fib(n-1)" [[.0,.1] PLUS, .0] "[fib(n),fib(n-1)"
package/index.coffee CHANGED
@@ -1,11 +1,25 @@
1
1
  fs = require "fs"
2
2
  {parse} = require "./parser.js"
3
3
  run = require "./run"
4
+ t = require "./codes"
5
+
6
+
7
+ finalize = (codes) ->
8
+ representatives = t.minimize(codes).representatives
9
+ codes = t.normalizeAll codes, representatives
10
+
11
+ {codes, representatives}
12
+
4
13
 
5
14
  compile = (script) ->
6
15
  do ({defs,exp} = parse script) ->
7
- run.defs = defs
16
+ # console.log defs
17
+ {codes, representatives} = finalize defs.codes
18
+ # console.log {codes, representatives}
19
+ run.defs = {codes, representatives, rels: defs.rels}
20
+
8
21
  (data) -> run exp, data
22
+
9
23
  compile.doc = "Transforms k-script (string) into a function"
10
24
 
11
25
  compile_file = (fileName) ->
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Generated by CoffeeScript 2.7.0
2
2
  (function() {
3
- var compile, compile_file, fs, parse, run, runScriptOnData;
3
+ var compile, compile_file, finalize, fs, parse, run, runScriptOnData, t;
4
4
 
5
5
  fs = require("fs");
6
6
 
@@ -8,9 +8,26 @@
8
8
 
9
9
  run = require("./run");
10
10
 
11
+ t = require("./codes");
12
+
13
+ finalize = function(codes) {
14
+ var representatives;
15
+ representatives = t.minimize(codes).representatives;
16
+ codes = t.normalizeAll(codes, representatives);
17
+ return {codes, representatives};
18
+ };
19
+
11
20
  compile = function(script) {
12
21
  return (function({defs, exp}) {
13
- run.defs = defs;
22
+ var codes, representatives;
23
+ // console.log defs
24
+ ({codes, representatives} = finalize(defs.codes));
25
+ // console.log {codes, representatives}
26
+ run.defs = {
27
+ codes,
28
+ representatives,
29
+ rels: defs.rels
30
+ };
14
31
  return function(data) {
15
32
  return run(exp, data);
16
33
  };
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "k-language for JSON-like data transformation",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "prepare": "coffee -c symbol-table.coffee; jison parser.jison",
8
8
  "prepublishOnly": "coffee -c *.coffee; echo '#!/usr/bin/env node' | cat - k.js > k.sh",
9
+ "all": "npm run prepublishOnly; npm run prepare; npm run prepublishOnly",
9
10
  "test": "coffee -c symbol-table.coffee; jison parser.jison; coffee test.coffee"
10
11
  },
11
12
  "author": "Wojciech Fraczak",
package/parser.jison CHANGED
@@ -44,27 +44,27 @@ str: STRING { $$ = String(yytext).slice(1,-1); };
44
44
  int: INT { $$ = parseInt(String(yytext)); };
45
45
 
46
46
  input_with_eof: defs comp EOF {
47
- result = {defs: $1, exp: $2};
47
+ result = {defs: {rels: s.rels, codes: s.codes}, exp: $2};
48
48
  // console.log(JSON.stringify(result, "", 2));
49
49
  return result;
50
50
  };
51
51
 
52
- defs: { $$ = {codes: {}, rels: {}}; }
53
- | defs name EQ comp SC { $1.rels[$2] = $4; $$ = $1; }
54
- | defs DOLLAR name EQ code SC { $1.codes[$3] = $5; $$ = $1; }
52
+ defs: { }
53
+ | defs name EQ comp SC { s.add_rel($2,$4); }
54
+ | defs DOLLAR name EQ code SC { s.add_code($3,$5); }
55
55
  ;
56
56
 
57
57
  code
58
58
  : name { $$ = {code: "ref", ref: $1}; }
59
59
  | LC labelled_codes RC { $$ = {code: "product",
60
60
  product: $2.reduce((r, lc) => {
61
- r[lc.label] = lc.code;
61
+ r[lc.label] = s.as_ref(lc.code);
62
62
  return r }
63
63
  , {})}; }
64
- | LB code RB { $$ = {code: "vector", vector: $2}; }
64
+ | LB code RB { $$ = {code: "vector", vector: s.as_ref($2)}; }
65
65
  | LA labelled_codes RA { $$ = {code: "union",
66
66
  union: $2.reduce((r, lc) => {
67
- r[lc.label] = lc.code;
67
+ r[lc.label] = s.as_ref(lc.code);
68
68
  return r }
69
69
  , {})}; }
70
70
  ;
@@ -95,7 +95,7 @@ comp
95
95
  exp
96
96
  : LC labelled RC { $$ = $2; }
97
97
  | LB list RB { $$ = {op: "vector", vector: $2}; }
98
- | LA list RA { $$ = {op: "union", union: $2}; }
98
+ | LA list RA { $$ = s.union($2); }
99
99
  | name { $$ = {op: "ref", ref: $1}; }
100
100
  | LP RP { $$ = s.identity; }
101
101
  | LP comp RP { $$ = $2; }
@@ -104,7 +104,7 @@ exp
104
104
  | DOT int { $$ = {op: "dot", dot: $2}; }
105
105
  | DOT str { $$ = {op: "dot", dot: $2}; }
106
106
  | DOT name { $$ = {op: "dot", dot: $2}; }
107
- | DOLLAR code { $$ = {op: "code", code: $2}; }
107
+ | DOLLAR code { $$ = {op: "code", code: s.as_ref($2)}; }
108
108
  ;
109
109
 
110
110
  labelled
package/parser.js CHANGED
@@ -94,19 +94,19 @@ case 3:
94
94
  break;
95
95
  case 4:
96
96
 
97
- result = {defs: $$[$0-2], exp: $$[$0-1]};
97
+ result = {defs: {rels: s.rels, codes: s.codes}, exp: $$[$0-1]};
98
98
  // console.log(JSON.stringify(result, "", 2));
99
99
  return result;
100
100
 
101
101
  break;
102
102
  case 5:
103
- this.$ = {codes: {}, rels: {}};
103
+
104
104
  break;
105
105
  case 6:
106
- $$[$0-4].rels[$$[$0-3]] = $$[$0-1]; this.$ = $$[$0-4];
106
+ s.add_rel($$[$0-3],$$[$0-1]);
107
107
  break;
108
108
  case 7:
109
- $$[$0-5].codes[$$[$0-3]] = $$[$0-1]; this.$ = $$[$0-5];
109
+ s.add_code($$[$0-3],$$[$0-1]);
110
110
  break;
111
111
  case 8:
112
112
  this.$ = {code: "ref", ref: $$[$0]};
@@ -114,17 +114,17 @@ break;
114
114
  case 9:
115
115
  this.$ = {code: "product",
116
116
  product: $$[$0-1].reduce((r, lc) => {
117
- r[lc.label] = lc.code;
117
+ r[lc.label] = s.as_ref(lc.code);
118
118
  return r }
119
119
  , {})};
120
120
  break;
121
121
  case 10:
122
- this.$ = {code: "vector", vector: $$[$0-1]};
122
+ this.$ = {code: "vector", vector: s.as_ref($$[$0-1])};
123
123
  break;
124
124
  case 11:
125
125
  this.$ = {code: "union",
126
126
  union: $$[$0-1].reduce((r, lc) => {
127
- r[lc.label] = lc.code;
127
+ r[lc.label] = s.as_ref(lc.code);
128
128
  return r }
129
129
  , {})};
130
130
  break;
@@ -156,7 +156,7 @@ case 23:
156
156
  this.$ = {op: "vector", vector: $$[$0-1]};
157
157
  break;
158
158
  case 24:
159
- this.$ = {op: "union", union: $$[$0-1]};
159
+ this.$ = s.union($$[$0-1]);
160
160
  break;
161
161
  case 25:
162
162
  this.$ = {op: "ref", ref: $$[$0]};
@@ -177,7 +177,7 @@ case 30: case 31: case 32:
177
177
  this.$ = {op: "dot", dot: $$[$0]};
178
178
  break;
179
179
  case 33:
180
- this.$ = {op: "code", code: $$[$0]};
180
+ this.$ = {op: "code", code: s.as_ref($$[$0])};
181
181
  break;
182
182
  case 34:
183
183
  this.$ = {op: "product", product: []};
package/run.coffee CHANGED
@@ -3,8 +3,8 @@ valid = (x) ->
3
3
  return x if x?
4
4
 
5
5
  builtin =
6
- "_log_": (arg) ->
7
- console.log "_log_: #{JSON.stringify arg}"
6
+ "_log!": (arg) ->
7
+ console.log "_log!: #{JSON.stringify arg}"
8
8
  arg
9
9
  "GT": (args) ->
10
10
  [ok,_] = do ([last,args...] = args) ->
@@ -48,11 +48,8 @@ codes =
48
48
  x is true or x is false
49
49
 
50
50
  verify = (code, value) ->
51
+ # console.log {code,value}
51
52
  switch code.code
52
- when "ref"
53
- do (c = run.defs.codes[code.ref]) ->
54
- return verify c, value if c?
55
- codes[code.ref] value
56
53
  when "vector"
57
54
  value.every (x) ->
58
55
  verify code.vector, x
@@ -65,8 +62,13 @@ verify = (code, value) ->
65
62
  do (fields = Object.keys(value)) ->
66
63
  return false unless fields.length is 1
67
64
  verify code.union[fields[0]], value[fields[0]]
65
+ else
66
+ do (c = run.defs.codes[run.defs.representatives[code]]) ->
67
+ return verify c, value if c?
68
+ codes[code] value
68
69
 
69
70
  run = (exp, value) ->
71
+ # console.log {exp,value}
70
72
  return undefined if value is undefined
71
73
  try
72
74
  switch exp.op
@@ -77,7 +79,7 @@ run = (exp, value) ->
77
79
  when "str", "int"
78
80
  return exp[exp.op]
79
81
  when "ref"
80
- do (defn = run.defs.rels[exp.ref]) ->
82
+ do (defn = run.defs.rels[exp.ref]?[0]) ->
81
83
  if defn?
82
84
  run defn, value
83
85
  else
package/run.js CHANGED
@@ -13,8 +13,8 @@
13
13
  };
14
14
 
15
15
  builtin = {
16
- "_log_": function(arg) {
17
- console.log(`_log_: ${JSON.stringify(arg)}`);
16
+ "_log!": function(arg) {
17
+ console.log(`_log!: ${JSON.stringify(arg)}`);
18
18
  return arg;
19
19
  },
20
20
  "GT": function(args) {
@@ -111,14 +111,8 @@
111
111
  };
112
112
 
113
113
  verify = function(code, value) {
114
+ // console.log {code,value}
114
115
  switch (code.code) {
115
- case "ref":
116
- return (function(c) {
117
- if (c != null) {
118
- return verify(c, value);
119
- }
120
- return codes[code.ref](value);
121
- })(run.defs.codes[code.ref]);
122
116
  case "vector":
123
117
  return value.every(function(x) {
124
118
  return verify(code.vector, x);
@@ -139,12 +133,20 @@
139
133
  }
140
134
  return verify(code.union[fields[0]], value[fields[0]]);
141
135
  })(Object.keys(value));
136
+ default:
137
+ return (function(c) {
138
+ if (c != null) {
139
+ return verify(c, value);
140
+ }
141
+ return codes[code](value);
142
+ })(run.defs.codes[run.defs.representatives[code]]);
142
143
  }
143
144
  };
144
145
 
145
146
  run = function(exp, value) {
146
- var e, i, len, ref, result;
147
+ var e, i, len, ref, ref1, result;
147
148
  if (value === void 0) {
149
+ // console.log {exp,value}
148
150
  return void 0;
149
151
  }
150
152
  try {
@@ -171,7 +173,7 @@
171
173
  return value;
172
174
  })(builtin[exp.ref](value));
173
175
  }
174
- })(run.defs.rels[exp.ref]);
176
+ })((ref = run.defs.rels[exp.ref]) != null ? ref[0] : void 0);
175
177
  case "dot":
176
178
  return value[exp.dot];
177
179
  case "comp":
@@ -192,9 +194,9 @@
192
194
  return result;
193
195
  });
194
196
  case "union":
195
- ref = exp.union;
196
- for (i = 0, len = ref.length; i < len; i++) {
197
- e = ref[i];
197
+ ref1 = exp.union;
198
+ for (i = 0, len = ref1.length; i < len; i++) {
199
+ e = ref1[i];
198
200
  result = (function() {
199
201
  try {
200
202
  return run(e, value);
@@ -1,8 +1,31 @@
1
+ rels = Object.create null
2
+ codes = Object.create null
3
+ codes["{}"] = {code: "product", product: {}}
4
+
1
5
  identity = op: "identity"
2
6
 
7
+ is_identity_rel = (rel) ->
8
+ rel.op is "identity"
9
+
10
+ is_empty_rel = (rel) ->
11
+ rel.op is "union" and rel.union.length is 0
12
+
13
+ is_full_rel = (rel) ->
14
+ switch rel.op
15
+ when "int", "str", "identity"
16
+ true
17
+ when "comp"
18
+ rel.comp.every is_full_rel
19
+ when "product", "vector"
20
+ Object.values(rel[rel.op]).every is_full_rel
21
+ else
22
+ false
23
+
3
24
  comp = (e1, e2) ->
4
- return e2 if e1.op is "identity"
5
- return e1 if e2.op is "identity"
25
+ return e2 if is_identity_rel e1
26
+ return e1 if is_identity_rel e2
27
+ return e1 if is_empty_rel e1
28
+ return e2 if is_empty_rel e2
6
29
  if e1.op is "comp" and e2.op is "comp"
7
30
  return {op: "comp", comp: [].concat(e1.comp,e2.comp)}
8
31
  if e1.op is "comp"
@@ -11,4 +34,31 @@ comp = (e1, e2) ->
11
34
  return {op: "comp", comp: [].concat([e1],e2.comp)}
12
35
  {op: "comp", comp: [e1,e2]}
13
36
 
14
- module.exports = {identity, comp};
37
+ union = (rels) ->
38
+ list = []
39
+ do ->
40
+ for rel in rels
41
+ new_rels = if rel.op is "union"
42
+ rel.union
43
+ else
44
+ [rel]
45
+ for rel in new_rels
46
+ list.push rel
47
+ return if is_full_rel rel
48
+ return list[0] if list.length is 1
49
+ {op: "union", union: list}
50
+
51
+ as_ref = (codeExp) ->
52
+ return codeExp.ref if codeExp.code is "ref"
53
+ newName = ":#{Object.keys(codes).length}"
54
+ codes[newName] = codeExp
55
+ return newName
56
+
57
+ add_rel = (name, rel) ->
58
+ rels[name] ?= []
59
+ rels[name].push rel
60
+
61
+ add_code = (name, code) ->
62
+ codes[name] = code
63
+
64
+ module.exports = {identity, comp, union, rels, codes, add_rel, add_code, as_ref};
package/symbol-table.js CHANGED
@@ -1,18 +1,57 @@
1
1
  // Generated by CoffeeScript 2.7.0
2
2
  (function() {
3
- var comp, identity;
3
+ var add_code, add_rel, as_ref, codes, comp, identity, is_empty_rel, is_full_rel, is_identity_rel, rels, union;
4
+
5
+ rels = Object.create(null);
6
+
7
+ codes = Object.create(null);
8
+
9
+ codes["{}"] = {
10
+ code: "product",
11
+ product: {}
12
+ };
4
13
 
5
14
  identity = {
6
15
  op: "identity"
7
16
  };
8
17
 
18
+ is_identity_rel = function(rel) {
19
+ return rel.op === "identity";
20
+ };
21
+
22
+ is_empty_rel = function(rel) {
23
+ return rel.op === "union" && rel.union.length === 0;
24
+ };
25
+
26
+ is_full_rel = function(rel) {
27
+ switch (rel.op) {
28
+ case "int":
29
+ case "str":
30
+ case "identity":
31
+ return true;
32
+ case "comp":
33
+ return rel.comp.every(is_full_rel);
34
+ case "product":
35
+ case "vector":
36
+ return Object.values(rel[rel.op]).every(is_full_rel);
37
+ default:
38
+ return false;
39
+ }
40
+ };
41
+
9
42
  comp = function(e1, e2) {
10
- if (e1.op === "identity") {
43
+ if (is_identity_rel(e1)) {
11
44
  return e2;
12
45
  }
13
- if (e2.op === "identity") {
46
+ if (is_identity_rel(e2)) {
14
47
  return e1;
15
48
  }
49
+ if (is_empty_rel(e1)) {
50
+ return e1;
51
+ }
52
+ if (is_empty_rel(e2)) {
53
+ return e2;
54
+ }
16
55
  if (e1.op === "comp" && e2.op === "comp") {
17
56
  return {
18
57
  op: "comp",
@@ -37,6 +76,53 @@
37
76
  };
38
77
  };
39
78
 
40
- module.exports = {identity, comp};
79
+ union = function(rels) {
80
+ var list;
81
+ list = [];
82
+ (function() {
83
+ var i, j, len, len1, new_rels, rel;
84
+ for (i = 0, len = rels.length; i < len; i++) {
85
+ rel = rels[i];
86
+ new_rels = rel.op === "union" ? rel.union : [rel];
87
+ for (j = 0, len1 = new_rels.length; j < len1; j++) {
88
+ rel = new_rels[j];
89
+ list.push(rel);
90
+ if (is_full_rel(rel)) {
91
+ return;
92
+ }
93
+ }
94
+ }
95
+ })();
96
+ if (list.length === 1) {
97
+ return list[0];
98
+ }
99
+ return {
100
+ op: "union",
101
+ union: list
102
+ };
103
+ };
104
+
105
+ as_ref = function(codeExp) {
106
+ var newName;
107
+ if (codeExp.code === "ref") {
108
+ return codeExp.ref;
109
+ }
110
+ newName = `:${Object.keys(codes).length}`;
111
+ codes[newName] = codeExp;
112
+ return newName;
113
+ };
114
+
115
+ add_rel = function(name, rel) {
116
+ if (rels[name] == null) {
117
+ rels[name] = [];
118
+ }
119
+ return rels[name].push(rel);
120
+ };
121
+
122
+ add_code = function(name, code) {
123
+ return codes[name] = code;
124
+ };
125
+
126
+ module.exports = {identity, comp, union, rels, codes, add_rel, add_code, as_ref};
41
127
 
42
128
  }).call(this);
package/typed-fib.k ADDED
@@ -0,0 +1,19 @@
1
+ inc = [(),1] PLUS;
2
+ fib2 =
3
+ $ { int target, int x, int "fib(x)", int "fib(x-1)" }
4
+ _log!
5
+ <
6
+ {[.target,.x] GT guard, () input}
7
+ .input
8
+ {.target target,
9
+ .x inc x,
10
+ [."fib(x)", ."fib(x-1)"] PLUS "fib(x)",
11
+ ."fib(x)" "fib(x-1)" }
12
+ fib2,
13
+ ."fib(x)"
14
+ >
15
+ $ int;
16
+
17
+ {() target, 0 x, 1 "fib(x)", 0 "fib(x-1)"} fib2
18
+
19
+