@fraczak/k 4.0.4 → 5.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/BIG_PICTURE.md CHANGED
@@ -14,19 +14,24 @@ Formally, a _type_ is a (subclass of) finite tree automata. The type defines a s
14
14
  which corresponds to the set of trees accepted by the automaton.
15
15
  Therefore, every type admits a unique normal form in terms of its minimal deterministic tree automaton.
16
16
 
17
- The set of all types can be seen as the universal deterministic (infinite) tree automaton.
18
- Every node of that automaton defines a type; only a finite set of nodes are reachable from
19
- any node.
20
-
21
17
  Types are defined by two constructs:
22
18
 
23
19
  1. Product
24
20
  2. Union (also called tagged or disjoint union)
25
21
 
22
+ ```bnf
23
+ type ::= name | '{' type_label_list '}' | '<' type_label_list '>'
24
+ type_label_list ::= /* empty */ | ( type label ',' )* type label
25
+ name ::= IDENTIFIER
26
+ label ::= STRING
27
+ ```
28
+
26
29
  Given a finite (possibly empty) set of types `t1, t2, ..., tn` and a set of pairwise different tags (labels) `l1, l2, ..., ln`,
27
30
 
28
- - by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n` projection functions `.l1`, `.l2`, `...`, `.ln`,
29
- mapping a value of type `{t1 l1, t2 l2, ..., tn ln}` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
31
+ - by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n`
32
+ _projection_ functions: `.l1`, `.l2`, `...`, `.ln`.
33
+ Each projection function is mapping a value of type `{t1 l1, t2 l2, ..., tn ln}`
34
+ into a value of type `t1`, `t2`, `...`, `tn`, respectively.
30
35
  - by `< t1 l1, t2 l2, ..., tn ln >`, we define a _union type_ with exactly `n` projection functions `.l1`, `.l2`, `...`, `.ln`,
31
36
  mapping a value of type `<t1 l1, t2 l2, ..., tn ln>` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
32
37
 
@@ -38,11 +43,37 @@ Some properties of these types:
38
43
 
39
44
  1. Singleton product and singleton union types, `{ t label }` and `< t label >`, are equivalent as they have
40
45
  the same set of values (and the same projection function `.label`). That's why, we not allow singleton product nor
41
- singleton union types in the syntax of the language. The other benefit is that we will be able to use the same systax
42
- for values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
46
+ singleton union types in the syntax of the language. The other benefit is that we will be able to use the same syntax
47
+ for writing out values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
43
48
  as a union type value (a variant).
44
- 2. The empty product type `{}` has a single value, called the _unit_ and also denoted by `{}`, which has no fields.
45
- 3. The empty union type `<>` has no values.
49
+ 2. The empty product type, `{}`, has a single value, called the _unit_
50
+ and also denoted by `{}`, which has no fields.
51
+ 3. The empty union type, `<>`, has no values.
52
+
53
+ ### Filters (patterns)
54
+
55
+ A _filter_ is a way of representing a _type class_, i.e, a set of types sharing some struture.
56
+ For example, if I wanted to refer to all product types that have a field `f` of type `T`,
57
+ I would write a pattern: `?{ T f, ... }`. In general, a filter is defined by:
58
+
59
+ ```bnf
60
+ filter ::= name | '$' type_expr | '{' filter_label_list '}'
61
+ | '<' filter_label_list '>' | '(' filter_label_list ')'
62
+ | filter '=' name
63
+ filter_label_list ::= /* empty */ | '...'
64
+ | ( filter label ',' ) * filter label
65
+ ```
66
+
67
+ Examples:
68
+
69
+ ```k-repl
70
+ X -- any type
71
+ ( ... ) -- any type
72
+ { ... } -- any product type
73
+ ( X x, X y ) -- any type with two fields of the same type
74
+ < X x, ... > = X -- a (recursive) type, 'X', with variant 'x'
75
+ -- of the same type 'X'
76
+ ```
46
77
 
47
78
  ### Partial functions
48
79
 
@@ -50,16 +81,19 @@ A _partial function_ is a function that is not defined for every value of its do
50
81
 
51
82
  There are three ways of combining partial functions:
52
83
 
53
- 1. __Composition__: we write a composition of `f` and `g` as `(f g)`, meaning that if `f` is defined on valye `x` producing `y`,
84
+ 1. __Composition__: we write a composition of `f` and `g` as `(f g)`,
85
+ meaning that if `f` is defined on value `x` producing `y`,
54
86
  and `g` is defined on value `y` producing `z`, then `(f g)` is defined on value `x` producing `z`.
55
87
  2. __Union__: we write `< f1, f2 >` for a union of two functions `f1` and `f2`, meaning that
56
88
  if `f1` is defined on value `x` producing `y`, then `< f1, f2 >`
57
89
  is defined on value `x` producing `y`. Otherwise, i.e., when `f1` is not defined for `x`, `< f1 f2 >` acts as `f2`.
58
- 3. __Product__: we write `{f1 lab1, f2 lab2 }` for a product of two functions `f1` and `f2`, meaning that if `f1` and `f2`
59
- are both defined on value `x` producing `y1` and `y2`, respectively, then `{f1 lab1, f2 lab2 }`
60
- is defined on value `x` producing `{y1 lab1, y2 lab2}`. Otherwise, `{f1 lab1, f2 lab2 }` is not defined for `x`.
90
+ 3. __Product__: we write `{f1 lab1, f2 lab2 }` for a product of two functions `f1` and `f2`,
91
+ meaning that if `f1` and `f2` are both defined on value `x` producing `y1` and `y2`,
92
+ respectively, then `{f1 lab1, f2 lab2 }` is defined on value `x` producing
93
+ `{y1 lab1, y2 lab2}`.
94
+ Otherwise, `{f1 lab1, f2 lab2 }` is not defined for `x`.
61
95
 
62
- Actually, all of the above operations take any number of arguments, not only two. For example,
96
+ Actually, composition, union, and product combine any number of partial functions, not only two. For example,
63
97
  empty composition, `()`, defines the identity function.
64
98
 
65
99
  ### Program
@@ -70,68 +104,66 @@ which is the partial function the program defines.
70
104
  The language syntax is defined by:
71
105
 
72
106
  ```bnf
73
- program ::= (type_definition | function_definition) * expression
74
-
107
+ program ::= (type_definition | function_definition)* expression
75
108
  type_definition ::= '$' type_name '=' type ';'
76
- type ::= '{' type_label_list '}' | '<' type_label_list '>'
77
- type_label_list ::= /* empty */ | ( type_expression label_name ',' ) + type_expression label_name
78
- type_expression ::= type_name | type
79
-
80
- function_definition ::= function_name '=' expression ';'
81
- expression ::= projection | typing | composition | union | product | function_name
82
- projection ::= '.' label_name
83
- typing ::= '$' tape_expression
109
+ function_definition ::= name '=' expression ';'
110
+ expression ::= name | '$' type | '?' filter
111
+ | projection | composition | union | product
112
+ projection ::= '.' label
84
113
  composition ::= '(' expression * ')'
85
114
  union ::= '<' (expression ',') + expression '>'
86
- product ::= '{' expression_label_list '}'
87
- expression_label_list ::= /* empty */ | ( expression label_name ',' ) * expression label
115
+ product ::= '{' expr_label_list '}'
116
+ expr_label_list ::= /* empty */
117
+ | (expression label_name ',')* expression label
88
118
  ```
89
119
 
90
- We assume that the `type_name`, `function_name`, and `label_name` are identifiers (strings) such that:
120
+ We assume that in a `kernel-core` program the names (for types, functions, and labels) are identifiers (strings) such that:
91
121
 
92
122
  1. All defined type names are distinct.
93
123
  2. All defined function names are distinct.
94
124
  3. All label names within a type definition or a `product` expression are locally distinct.
95
125
 
96
- The `typing` expression acts as a type annotation and can be seen as a 'filter', i.e., an identity
97
- function defined only for the values of the corresponding type.
126
+ `Filter` or `type` expressions act as a type annotation, i.e., an identity
127
+ function defined only for the values of the corresponding types.
98
128
 
99
129
  That's it.
130
+
100
131
  No `builtin` types, no `if` statement, no `loop`, no `throw`, no _closure_, no _annotations_, and no macros.
101
- Just types and partial functions.
132
+ Just _types_, _filters_, and _functions_.
102
133
 
103
134
  ### Normalization
104
135
 
105
136
  All type names are replaced by the hash of their canonical representation.
106
137
 
107
- We will also replace the function name by the hash of the normalized unfolded definition of the function.
108
- Obviously, two different definitions of the same function may have different hashes.
138
+ We replace the function name by the hash of the normalized definition of the function as provided by the program defining the function.
139
+ Obviously, a function may have many different definitions so the hash-based names are not canonical. By using hash-based function names we avoid the problem of name clashes when bringing together many `kernel-core` programs.
109
140
 
110
- This approach solves the problem of modules and imports and opens the door to a universal
141
+ This approach solves the problem of modules, imports, etc., and opens the door to a universal
111
142
  registry of types (schemas) and functions.
112
143
 
113
144
  ## Typing and Polymorphism (codes and filters/patterns)
114
145
 
115
- Abstract Syntax Tree of a program after parsing consists of a dictionary of _type definitions_,
146
+ Abstract Syntax Tree of a program consists of a dictionary of _type definitions_,
116
147
  a dictionary of _function definitions_, and the final _main expression_.
117
148
 
118
149
  In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
119
150
  the program on an input.
120
151
 
121
- Our objective however is to compute canonicals representations for types and normalized
122
- representations for functions, so they could be effectively reused.
152
+ Our objective however is to compute canonical representations for types and normalized
153
+ representations for functions, so they could be reused.
123
154
 
124
155
  The normalization steps are:
125
156
 
126
157
  1. Build the _type graph_ from all types used by the program, i.e., used in _type definitions_
127
158
  as well as type expressions used in _function definitions_ and the _main expression_.
128
- 2. Annotate the expressions with types or type patterns; every node of the AST is annotated with a pair of type patterns.
129
- 3. Turn the _singleton_ type patterns into types and add them to the _type graph_. Go to (2) unless no change.
130
- 4. Normalize the function definitions by rewriting them as the fix-point of a single equation.
159
+ 2. Annotate the expressions with filters; every node of the AST is annotated with a pair of filters.
160
+ 3. Turn the _singleton_ filters into types and add them to the _type graph_. Go to (2) unless no change.
131
161
 
132
- ### Example
162
+ ### Examples
133
163
 
134
- ```k
164
+ #### Example 1
165
+
166
+ ```k-repl
135
167
  $ bit = < {} o, {} i >;
136
168
  bit0 = {{} o} $bit;
137
169
  bit1 = {{} i} $bit;
@@ -169,7 +201,7 @@ and a type `byte` as a product, for simplicity, of four `bit`s.
169
201
  Functions `bit0`, `bit1`, and `zero` are "constant polymorphic functions", meaning:
170
202
 
171
203
  - constant: if defined, they always return exactly the same value;
172
- - polymorphic: they are defined for more than one pair of input and output type: functions
204
+ - polymorphic: they are defined for more than one pair of input and output types: functions
173
205
  `bit0` and `bit1` are of type `?X -> $bit`, and `zero` is of type `?X -> $byte`, where `?X`
174
206
  denotes an unconstrained type pattern, also denoted as `?(...)`.
175
207
 
@@ -186,15 +218,42 @@ with following constraints:
186
218
  > `?( V i, ...)`;
187
219
  - `V` is unconstrained, denoted by `(...)`;
188
220
 
189
- We could write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
221
+ We can write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
190
222
 
191
223
  The target type `${ byte byte, bit overflown }` was derived from pattern `?{ $byte byte, $bit overflown }`.
192
- Such a pettern is called _singleton pattern_ as only one type fits the pattern.
224
+ Such a pattern is called _singleton pattern_, as only one type fits the pattern.
225
+
226
+ #### Example 2
227
+
228
+ Polymorphic list functions:
229
+
230
+ ```k-repl
231
+ list? = ?< {} nil, {X car, Y cdr} cons > = Y;
232
+ nil = {{} nil} list?;
233
+ singleton = {{() car, nil cdr} cons} list?;
234
+ cons = {{.car car, .cdr cdr} cons} list?;
235
+ nil? = list? .nil nil;
236
+ car = list? .cons .car;
237
+ cdr = list? .cons .cdr;
238
+ ```
239
+
240
+ For example, the filter annotation for `car` is:
241
+
242
+ ```k-repl
243
+ --R car
244
+ {
245
+ name: 'yRXGSJrIWCuKV',
246
+ type: '?<{X1 car, X0 cdr} cons, $KL nil>=X0 --> ?X1',
247
+ def: '?<{X1 car, X0 cdr} cons, $KL nil>=X0 .cons .car ?X1'
248
+ }
249
+ --C KL
250
+ $ KL = {}; -- $C0={};
251
+ ```
193
252
 
194
253
  ## Universal Schema Registry
195
254
 
196
255
  Since the normalization process for types is fast and deterministic, we can build a universal
197
- schema registry that will store all invented types.
256
+ schema registry that will store all invented types.
198
257
  The registry will be a key-value store, where the key is the hash of the normalized type,
199
258
  and the value is the normalized type itself.
200
259
 
@@ -203,8 +262,7 @@ definition and stored in the similar key-value store.
203
262
 
204
263
  Non-polymorphic functions can be easily indexed by the hashes of their input and output types
205
264
  so that we can quickly find the function we need.
206
- Searching for polymorphic functions is more complicated, as we need to find the function
207
- whose input and output type patterns fit given types.
265
+ Indexing polymorphic functions seems complicated (TODO).
208
266
 
209
267
  ## Serialization and Compilation
210
268
 
@@ -5,7 +5,7 @@ import assert from 'assert';
5
5
  t(fs.readFileSync("./Examples/bnat-patterns.k").toString("utf8"), (annotated) => {
6
6
  // console.log(JSON.stringify(annotated,null,2));
7
7
  const {input,output} = in_out(annotated);
8
- // console.log(output);
8
+ console.log(output);
9
9
  assert.equal(output.type, annotated.representatives["bnat"]);
10
10
  assert.equal(input.type, annotated.representatives["pair"]);
11
11
  console.log("OK");
package/TODO.md ADDED
@@ -0,0 +1,18 @@
1
+ # To do list for canonical names
2
+
3
+ - [ ] Write a script to search for conflicting names
4
+ - [ ] Export a relation. i.e., rewrite it as a list of all
5
+ dependency definitions using canonical names
6
+ - [ ] Find a way of adding 'comments' or some way of 'tagging' the definitions
7
+ - [X] The definitions should be simplied by removing useless filters
8
+ - [X] Definitions, which are just aliases to non built-in rels should not introduce a new canonical name
9
+ - [ ] Can we inline some references w/o changing the canonical name?
10
+
11
+ ## Simplification rules
12
+
13
+ It seems to me that after calculating input and output patterns, all
14
+ filters and code expressions could be replaced by just input and output
15
+ filters corresponding to the patterns. [DONE]
16
+
17
+ The filer variables have to be renamed for normalization if they take part of
18
+ the canonical name calculation. [DONE]
package/TYPING.md CHANGED
@@ -360,6 +360,6 @@ Repeat the procedure until the type pattern graph is not changing anymore.
360
360
  - [X] Redefine built-in CONS and SNOC to allign with no support for non-homogeneous lists
361
361
  - [X] Integrate filters into typePatters (execution is not affected)
362
362
  - [/] better type error repporting - the info exists in the pattern parent tree ... in progress
363
- - [ ] Introduce a space/time bounds for type derivation
363
+ - [X] Introduce a space/time bounds for type derivation
364
364
  - [ ] Check for loops, e.g., `$x = < x f, ...>`, `$x = [x]`, `$x = {x f, ...}` -- this
365
- looks a little arbitrary, but maybe this is actually an important part
365
+ looks a little arbitrary, but maybe this is actually an important part
package/export.mjs ADDED
@@ -0,0 +1,185 @@
1
+
2
+ import { prettyRel, patterns2filters } from "./pretty.mjs";
3
+ import { hash } from "./hash.mjs";
4
+
5
+ function simplifyRel(relDef,rels) {
6
+ // remove filters and codes
7
+ const prune = (rel) => {
8
+ const newRel = {...rel};
9
+ switch (rel.op) {
10
+ case "product":
11
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: prune(exp)}));
12
+ break;
13
+ case "union":
14
+ newRel.union = rel.union.map(exp => prune(exp));
15
+ break;
16
+ case "comp":
17
+ newRel.comp = rel.comp.reduce((list, exp) => {
18
+ const newExp = prune(exp);
19
+ if (newExp.op != "identity")
20
+ list.push(newExp);
21
+ return list;
22
+ },[]);
23
+ if (newRel.comp.length == 0)
24
+ newRel.op = "identity";
25
+ break;
26
+ case "vector":
27
+ newRel.vector = rel.vector.map(exp => prune(exp));
28
+ break;
29
+ case "caret":
30
+ newRel.caret = prune(rel.caret);
31
+ break;
32
+ case 'code':
33
+ case 'filter':
34
+ newRel.op = "identity";
35
+ break;
36
+ case 'ref':
37
+ // console.log("ref", rel.ref, rels[rel.ref]);
38
+ if (["filter","code","identity"].includes(rels[rel.ref]?.def.op)){
39
+ newRel.op = "identity";
40
+ };
41
+ break;
42
+ default:
43
+ break;
44
+ };
45
+ return newRel;
46
+ };
47
+
48
+ const rel = relDef.def;
49
+ const filters = patterns2filters(relDef.typePatternGraph, ...rel.patterns).map( filter =>
50
+ ({op: "filter", filter: filter}));
51
+ const newRel = prune(rel);
52
+ let resultRel = {...newRel};
53
+ if (newRel.op == "identity")
54
+ return {...resultRel, ...filters[0]};
55
+
56
+ if (newRel.op == "comp") {
57
+ resultRel.comp = [ filters[0], ...newRel.comp, filters[1] ];
58
+ return resultRel;
59
+ }
60
+ return {...resultRel,
61
+ op: "comp",
62
+ comp: [filters[0], newRel, filters[1] ]
63
+ };
64
+ };
65
+
66
+
67
+ function theID(alias, rel, scc, name) {
68
+ const sccNames = new Set(scc);
69
+ const auxNames = {[name]: "X0"};
70
+ // rename and remove filters and codes
71
+ const reNameX = (rel) => {
72
+ const newRel = {...rel};
73
+ switch (rel.op) {
74
+ case "product":
75
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: reNameX(exp)}));
76
+ break;
77
+ case "union":
78
+ newRel.union = rel.union.map(exp => reNameX(exp));
79
+ break;
80
+ case "comp":
81
+ newRel.comp = rel.comp.map(exp => reNameX(exp));
82
+ break;
83
+ case "vector":
84
+ newRel.vector = rel.vector.map(exp => reNameX(exp));
85
+ break;
86
+ case "caret":
87
+ newRel.caret = reNameX(rel.caret);
88
+ break;
89
+ case "ref": {
90
+ const n = rel.ref;
91
+ if (alias[n] != undefined) {
92
+ newRel.ref = alias[n];
93
+ } else if (auxNames[n] != undefined) {
94
+ newRel.ref = auxNames[n];
95
+ } else if (sccNames.has(n)) {
96
+ auxNames[n] = `X${Object.keys(auxNames).length}`;
97
+ newRel.ref = auxNames[n];
98
+ };
99
+ }; break;
100
+ default:
101
+ break;
102
+ };
103
+ return newRel;
104
+ };
105
+
106
+ const newRel = reNameX(rel);
107
+ const resultRelStr = prettyRel(newRel);
108
+ // console.log(` ${name} = ${resultRelStr}`);
109
+ const newName = hash(resultRelStr);
110
+ return newName;
111
+ };
112
+
113
+ function assignCanonicalNames(scc, rels, relAlias) {
114
+ const newAlias = scc.reduce( (newAlias,relName) => {
115
+ const relDef = rels[relName];
116
+ if (relDef.def.op == "ref" && rels[relDef.def.ref] != undefined) {
117
+ // inlining if direct alias to a non built-in relation
118
+ // relDef.def = rels[relDef.def.ref]; // simplifyRel(rels[relDef.def.ref],rels);
119
+ rels[relName] = rels[relDef.def.ref];
120
+ } else {
121
+ relDef.def = simplifyRel(relDef,rels);
122
+ }
123
+ newAlias[relName] = theID(relAlias, rels[relName].def, scc, relName);
124
+ return newAlias;
125
+ }, {});
126
+ const newNames = [...new Set(Object.values(newAlias))];
127
+ // console.log(` --- SCC: {${scc.join(",")}} ---`);
128
+
129
+ const sccCanonicalName = newNames.sort().join(":");
130
+ for (let relName in newAlias) {
131
+ if (newNames.length > 1)
132
+ relAlias[relName] = hash(newAlias[relName]+":"+sccCanonicalName);
133
+ else
134
+ relAlias[relName] = newAlias[relName];
135
+ // console.log(` ${relName} -> ${relAlias[relName]}`);
136
+ };
137
+ }
138
+
139
+ function exportRelation(rels, alias, name) {
140
+ const queue = [name];
141
+ const inQueue = { [name]: true };
142
+ const result = {};
143
+ const reName = (rel) => {
144
+ const newRel = {...rel};
145
+ switch (rel.op) {
146
+ case "product":
147
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: reName(exp)}));
148
+ break;
149
+ case "union":
150
+ newRel.union = rel.union.map(exp => reName(exp));
151
+ break;
152
+ case "comp":
153
+ newRel.comp = rel.comp.map(exp => reName(exp));
154
+ break;
155
+ case "vector":
156
+ newRel.vector = rel.vector.map(exp => reName(exp));
157
+ break;
158
+ case "caret":
159
+ newRel.caret = reName(rel.caret);
160
+ break;
161
+ case "ref": {
162
+ const n = rel.ref;
163
+ if (alias[n] == undefined) // built-in
164
+ return newRel;
165
+ newRel.ref = alias[n];
166
+ if (! inQueue[n]) {
167
+ inQueue[n] = true;
168
+ queue.push(n);
169
+ }
170
+ }; break;
171
+ default:
172
+ break;
173
+ };
174
+ return newRel;
175
+ };
176
+ while (queue.length > 0 ) {
177
+ const name = queue.shift();
178
+ const canonicalName = alias[name];
179
+ result[canonicalName] = {...rels[name], def: reName(rels[name].def), name: name};
180
+ }
181
+ return result;
182
+ }
183
+
184
+ export default { assignCanonicalNames, exportRelation };
185
+ export { assignCanonicalNames, exportRelation };
package/index.mjs CHANGED
@@ -4,19 +4,7 @@ import run from "./run.mjs";
4
4
  import t from "./codes.mjs";
5
5
 
6
6
  function compile(script) {
7
- try {
8
- run.defs = annotate(script);
9
- } catch (e) {
10
- console.error(e.message);
11
- console.error("WARN: Recompiling without type reconciliation due to the type error above.");
12
- const { defs, exp } = parse(script);
13
- const { codes, representatives } = t.finalize(defs.codes);
14
- run.defs = {
15
- rels: {...defs.rels, "__main__": {def: exp}},
16
- codes, representatives
17
- };
18
-
19
- }
7
+ run.defs = annotate(script);
20
8
  return run.bind(null, run.defs.rels.__main__.def);
21
9
  }
22
10
 
@@ -35,9 +23,9 @@ function annotate(script) {
35
23
  const representatives = t.register(defs.codes);
36
24
  const rels = {...defs.rels, "__main__": {def: exp}};
37
25
 
38
- patterns(representatives, rels);
26
+ const relAlias = patterns(representatives, rels);
39
27
 
40
- return {rels, representatives}
28
+ return {rels, representatives, relAlias};
41
29
  }
42
30
  annotate.doc = "Annotate all the script expressions with patterns";
43
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "4.0.4",
3
+ "version": "5.0.1",
4
4
  "description": "k-language for JSON-like data transformation",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
package/parser.jison CHANGED
@@ -43,7 +43,7 @@ function fromEscString(escString) {
43
43
 
44
44
  %%
45
45
  [/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/] /* c-comment */
46
- ("//"|"#"|"%"|"--")[^\n]* /* one line comment */
46
+ ("//"|"#"|"%"|"--"|"\\")[^\n]* /* one line comment */
47
47
  \s+ /* blanks */
48
48
  "<" return 'LA';
49
49
  "{" return 'LC';
package/parser.mjs CHANGED
@@ -846,7 +846,7 @@ case 25:return 47;
846
846
  break;
847
847
  }
848
848
  },
849
- rules: [/^(?:[/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/])/,/^(?:(\/\/|#|%|--)[^\n]*)/,/^(?:\s+)/,/^(?:<)/,/^(?:\{)/,/^(?:\[)/,/^(?:\()/,/^(?:>)/,/^(?:\})/,/^(?:\])/,/^(?:\))/,/^(?:=)/,/^(?:\.\.\.)/,/^(?:\.)/,/^(?:,)/,/^(?:;)/,/^(?::)/,/^(?:\$)/,/^(?:\?)/,/^(?:\|)/,/^(?:\^)/,/^(?:"([^"\\]|\\(.|\n))*"|'([^'\\]|\\(.|\n))*')/,/^(?:[a-zA-Z_][a-zA-Z0-9_?!]*)/,/^(?:0|[-]?[1-9][0-9]*)/,/^(?:\+\+\+)/,/^(?:$)/],
849
+ rules: [/^(?:[/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/])/,/^(?:(\/\/|#|%|--|\\)[^\n]*)/,/^(?:\s+)/,/^(?:<)/,/^(?:\{)/,/^(?:\[)/,/^(?:\()/,/^(?:>)/,/^(?:\})/,/^(?:\])/,/^(?:\))/,/^(?:=)/,/^(?:\.\.\.)/,/^(?:\.)/,/^(?:,)/,/^(?:;)/,/^(?::)/,/^(?:\$)/,/^(?:\?)/,/^(?:\|)/,/^(?:\^)/,/^(?:"([^"\\]|\\(.|\n))*"|'([^'\\]|\\(.|\n))*')/,/^(?:[a-zA-Z_][a-zA-Z0-9_?!]*)/,/^(?:0|[-]?[1-9][0-9]*)/,/^(?:\+\+\+)/,/^(?:$)/],
850
850
  conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"inclusive":true}}
851
851
  });
852
852
  return lexer;
package/patterns.mjs CHANGED
@@ -3,6 +3,7 @@ import hash from "./hash.mjs";
3
3
  import { TypePatternGraph } from "./typing.mjs";
4
4
  import { Graph, sccs, topoOrder } from "./Graph.mjs";
5
5
  import codes from "./codes.mjs";
6
+ import { assignCanonicalNames } from "./export.mjs";
6
7
 
7
8
  const unitCode = codes.unitCode;
8
9
 
@@ -97,10 +98,11 @@ function compactRel(relDef, name = "") {
97
98
 
98
99
  function patterns(representatives, rels) {
99
100
  // INPUT:
100
- // codes: {"BG": {"code": "product", "product": {}}, ...}
101
- // representatives:{"{}": "BG", ...}
101
+ // codes: {"KL": {"code": "product", "product": {}}, ...}
102
+ // representatives:{"{}": "KL", ...}
102
103
  // rels: {"rlz": [{op: comp,...}, ...], ...}
103
104
 
105
+ const relAlias = {};
104
106
 
105
107
  // 1 INITIALIZATION
106
108
  // 1.1 initialize patternNodes and rels
@@ -405,7 +407,7 @@ function patterns(representatives, rels) {
405
407
  rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
406
408
  break;
407
409
  default:
408
- throw new Error(`No definition found for ${rel.ref}`);
410
+ throw new Error(`No definition found for '${rel.ref}'`);
409
411
  }
410
412
  }
411
413
 
@@ -416,7 +418,7 @@ function patterns(representatives, rels) {
416
418
  for (const relName in rels) {
417
419
  const rootDef = rels[relName];
418
420
  rootDef.typePatternGraph = new TypePatternGraph();
419
- rootDef.varRefs = []; // the list of non-built references as pointers to AST nodes
421
+ rootDef.varRefs = []; // the list of references to non-builtin rels as pointers to AST nodes
420
422
  rootDef.patternVars = {};
421
423
  augment(rootDef.def, rootDef);
422
424
  delete rootDef.patternVars;
@@ -453,12 +455,16 @@ function patterns(representatives, rels) {
453
455
 
454
456
  // 3. Topological sort of the DAG
455
457
 
456
- const sccInOrder = topoOrder(DAG);
458
+ const sccInOrder = topoOrder(DAG).reverse();
457
459
 
458
460
  // 4. For each strongly connected component C in a bottom-up order
461
+
462
+ // we will also generate the canonical representation of the relations in C and replace
463
+ // the name to the relation by the hash of the canonical representation
464
+
465
+ // console.log("----- SCC in order", sccInOrder);
459
466
 
460
- for (const scc of sccInOrder.reverse()) {
461
- // console.log(`SCC: { ${DAGnodes[scc].scc} }`);
467
+ for (const scc of sccInOrder) {
462
468
 
463
469
  const maxNumberOfIterations = 10;
464
470
 
@@ -497,9 +503,7 @@ function patterns(representatives, rels) {
497
503
  throw e;
498
504
  }
499
505
  }
500
-
501
506
  rels[relName] = compactRel(relDef,relName);
502
-
503
507
  return relDefToString(rels[relName]);
504
508
  }));
505
509
 
@@ -507,9 +511,19 @@ function patterns(representatives, rels) {
507
511
  // console.log("SUCCESS!");
508
512
  break;
509
513
  }
510
- } // while
511
-
514
+ } // end of iteration loop
515
+
516
+ DAGnodes[scc].scc.forEach( relName => {
517
+ const relDef = rels[relName];
518
+ const canonical = relDefToString(relDef);
519
+ const h = hash(canonical);
520
+ relAlias[relName] = h;
521
+ });
522
+
523
+ assignCanonicalNames(DAGnodes[scc].scc, rels, relAlias);
524
+
512
525
  }
526
+ return relAlias;
513
527
  }
514
528
 
515
529
  export default { patterns };