@fraczak/k 4.0.4 → 5.0.0

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
@@ -25,8 +25,10 @@ Types are defined by two constructs:
25
25
 
26
26
  Given a finite (possibly empty) set of types `t1, t2, ..., tn` and a set of pairwise different tags (labels) `l1, l2, ..., ln`,
27
27
 
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.
28
+ - by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n`
29
+ _projection_ functions: `.l1`, `.l2`, `...`, `.ln`.
30
+ Each projection function is mapping a value of type `{t1 l1, t2 l2, ..., tn ln}`
31
+ into a value of type `t1`, `t2`, `...`, `tn`, respectively.
30
32
  - by `< t1 l1, t2 l2, ..., tn ln >`, we define a _union type_ with exactly `n` projection functions `.l1`, `.l2`, `...`, `.ln`,
31
33
  mapping a value of type `<t1 l1, t2 l2, ..., tn ln>` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
32
34
 
@@ -38,8 +40,8 @@ Some properties of these types:
38
40
 
39
41
  1. Singleton product and singleton union types, `{ t label }` and `< t label >`, are equivalent as they have
40
42
  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 }`,
43
+ singleton union types in the syntax of the language. The other benefit is that we will be able to use the same syntax
44
+ for writing out values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
43
45
  as a union type value (a variant).
44
46
  2. The empty product type `{}` has a single value, called the _unit_ and also denoted by `{}`, which has no fields.
45
47
  3. The empty union type `<>` has no values.
@@ -50,7 +52,7 @@ A _partial function_ is a function that is not defined for every value of its do
50
52
 
51
53
  There are three ways of combining partial functions:
52
54
 
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`,
55
+ 1. __Composition__: we write a composition of `f` and `g` as `(f g)`, meaning that if `f` is defined on value `x` producing `y`,
54
56
  and `g` is defined on value `y` producing `z`, then `(f g)` is defined on value `x` producing `z`.
55
57
  2. __Union__: we write `< f1, f2 >` for a union of two functions `f1` and `f2`, meaning that
56
58
  if `f1` is defined on value `x` producing `y`, then `< f1, f2 >`
@@ -59,7 +61,7 @@ There are three ways of combining partial functions:
59
61
  are both defined on value `x` producing `y1` and `y2`, respectively, then `{f1 lab1, f2 lab2 }`
60
62
  is defined on value `x` producing `{y1 lab1, y2 lab2}`. Otherwise, `{f1 lab1, f2 lab2 }` is not defined for `x`.
61
63
 
62
- Actually, all of the above operations take any number of arguments, not only two. For example,
64
+ Actually, composition, union, and product combine any number of partial functions, not only two. For example,
63
65
  empty composition, `()`, defines the identity function.
64
66
 
65
67
  ### Program
@@ -78,16 +80,21 @@ type_label_list ::= /* empty */ | ( type_expression label_name ',' ) + type_expr
78
80
  type_expression ::= type_name | type
79
81
 
80
82
  function_definition ::= function_name '=' expression ';'
81
- expression ::= projection | typing | composition | union | product | function_name
83
+ expression ::= projection | typing | filter | composition | union | product | function_name
82
84
  projection ::= '.' label_name
83
- typing ::= '$' tape_expression
85
+ typing ::= '$' type_expression
86
+ filter ::= '?' filter_expression
87
+ filter_expression ::= $ type_expression | filter_name
88
+ | '{' filter_label_list '}' | '<' filter_label_list '>' | '(' filter_label_list ')'
89
+ | filter_expression '=' filter_name
90
+ filter_label_list ::= /* empty */ | ( filter_expression label_name ',' ) * filter_expression label
84
91
  composition ::= '(' expression * ')'
85
92
  union ::= '<' (expression ',') + expression '>'
86
93
  product ::= '{' expression_label_list '}'
87
94
  expression_label_list ::= /* empty */ | ( expression label_name ',' ) * expression label
88
95
  ```
89
96
 
90
- We assume that the `type_name`, `function_name`, and `label_name` are identifiers (strings) such that:
97
+ We assume that in a `kernel-core` program the `type_name`, `function_name`, and `label_name` are identifiers (strings) such that:
91
98
 
92
99
  1. All defined type names are distinct.
93
100
  2. All defined function names are distinct.
@@ -97,6 +104,7 @@ The `typing` expression acts as a type annotation and can be seen as a 'filter',
97
104
  function defined only for the values of the corresponding type.
98
105
 
99
106
  That's it.
107
+
100
108
  No `builtin` types, no `if` statement, no `loop`, no `throw`, no _closure_, no _annotations_, and no macros.
101
109
  Just types and partial functions.
102
110
 
@@ -104,22 +112,22 @@ Just types and partial functions.
104
112
 
105
113
  All type names are replaced by the hash of their canonical representation.
106
114
 
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.
115
+ We replace the function name by the hash of the normalized definition of the function as provided by the program defining the function.
116
+ 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
117
 
110
- This approach solves the problem of modules and imports and opens the door to a universal
118
+ This approach solves the problem of modules, imports, etc., and opens the door to a universal
111
119
  registry of types (schemas) and functions.
112
120
 
113
121
  ## Typing and Polymorphism (codes and filters/patterns)
114
122
 
115
- Abstract Syntax Tree of a program after parsing consists of a dictionary of _type definitions_,
123
+ Abstract Syntax Tree of a program consists of a dictionary of _type definitions_,
116
124
  a dictionary of _function definitions_, and the final _main expression_.
117
125
 
118
126
  In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
119
127
  the program on an input.
120
128
 
121
129
  Our objective however is to compute canonicals representations for types and normalized
122
- representations for functions, so they could be effectively reused.
130
+ representations for functions, so they could be reused.
123
131
 
124
132
  The normalization steps are:
125
133
 
@@ -127,7 +135,7 @@ The normalization steps are:
127
135
  as well as type expressions used in _function definitions_ and the _main expression_.
128
136
  2. Annotate the expressions with types or type patterns; every node of the AST is annotated with a pair of type patterns.
129
137
  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.
138
+
131
139
 
132
140
  ### Example
133
141
 
@@ -169,7 +177,7 @@ and a type `byte` as a product, for simplicity, of four `bit`s.
169
177
  Functions `bit0`, `bit1`, and `zero` are "constant polymorphic functions", meaning:
170
178
 
171
179
  - 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
180
+ - polymorphic: they are defined for more than one pair of input and output types: functions
173
181
  `bit0` and `bit1` are of type `?X -> $bit`, and `zero` is of type `?X -> $byte`, where `?X`
174
182
  denotes an unconstrained type pattern, also denoted as `?(...)`.
175
183
 
@@ -186,15 +194,15 @@ with following constraints:
186
194
  > `?( V i, ...)`;
187
195
  - `V` is unconstrained, denoted by `(...)`;
188
196
 
189
- We could write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
197
+ We can write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
190
198
 
191
199
  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.
200
+ Such a pattern is called _singleton pattern_, as only one type fits the pattern.
193
201
 
194
202
  ## Universal Schema Registry
195
203
 
196
204
  Since the normalization process for types is fast and deterministic, we can build a universal
197
- schema registry that will store all invented types.
205
+ schema registry that will store all invented types.
198
206
  The registry will be a key-value store, where the key is the hash of the normalized type,
199
207
  and the value is the normalized type itself.
200
208
 
@@ -203,8 +211,7 @@ definition and stored in the similar key-value store.
203
211
 
204
212
  Non-polymorphic functions can be easily indexed by the hashes of their input and output types
205
213
  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.
214
+ Indexing polymorphic functions seems complicated (TODO).
208
215
 
209
216
  ## Serialization and Compilation
210
217
 
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,177 @@
1
+
2
+ import { prettyRel, patterns2filters } from "./pretty.mjs";
3
+ import { hash } from "./hash.mjs";
4
+
5
+ function simplifyRel(relDef) {
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
+ default:
36
+ break;
37
+ };
38
+ return newRel;
39
+ };
40
+
41
+ const rel = relDef.def;
42
+ const filters = patterns2filters(relDef.typePatternGraph, ...rel.patterns).map( filter =>
43
+ ({op: "filter", filter: filter}));
44
+ const newRel = prune(rel);
45
+ let resultRel = {...newRel};
46
+ if (newRel.op == "identity")
47
+ return filters[0];
48
+
49
+ if (newRel.op == "comp") {
50
+ resultRel.comp = [ filters[0], ...newRel.comp, filters[1] ];
51
+ return resultRel;
52
+ }
53
+ return {
54
+ op: "comp",
55
+ comp: [filters[0], newRel, filters[1] ]
56
+ };
57
+ };
58
+
59
+
60
+ function theID(alias, rel, scc, name) {
61
+ const sccNames = new Set(scc);
62
+ const auxNames = {[name]: "X0"};
63
+ // rename and remove filters and codes
64
+ const reNameX = (rel) => {
65
+ const newRel = {...rel};
66
+ switch (rel.op) {
67
+ case "product":
68
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: reNameX(exp)}));
69
+ break;
70
+ case "union":
71
+ newRel.union = rel.union.map(exp => reNameX(exp));
72
+ break;
73
+ case "comp":
74
+ newRel.comp = rel.comp.map(exp => reNameX(exp));
75
+ break;
76
+ case "vector":
77
+ newRel.vector = rel.vector.map(exp => reNameX(exp));
78
+ break;
79
+ case "caret":
80
+ newRel.caret = reNameX(rel.caret);
81
+ break;
82
+ case "ref": {
83
+ const n = rel.ref;
84
+ if (alias[n] != undefined) {
85
+ newRel.ref = alias[n];
86
+ } else if (auxNames[n] != undefined) {
87
+ newRel.ref = auxNames[n];
88
+ } else if (sccNames.has(n)) {
89
+ auxNames[n] = `X${Object.keys(auxNames).length}`;
90
+ newRel.ref = auxNames[n];
91
+ };
92
+ }; break;
93
+ default:
94
+ break;
95
+ };
96
+ return newRel;
97
+ };
98
+
99
+ const newRel = reNameX(rel);
100
+ const resultRelStr = prettyRel(newRel);
101
+ // console.log(` ${name} = ${resultRelStr}`);
102
+ const newName = hash(resultRelStr);
103
+ return newName;
104
+ };
105
+
106
+ function assignCanonicalNames(scc, rels, relAlias) {
107
+ const newAlias = scc.reduce( (newAlias,relName) => {
108
+ const relDef = rels[relName];
109
+ if (relDef.def.op == "ref" && rels[relDef.def.ref] != undefined) {
110
+ // inlining if direct alias to a non built-in relation
111
+ relDef.simplified = simplifyRel(rels[relDef.def.ref]);
112
+ } else {
113
+ relDef.simplified = simplifyRel(relDef);
114
+ }
115
+ newAlias[relName] = theID(relAlias, relDef.simplified, scc, relName);
116
+ return newAlias;
117
+ }, {});
118
+ const newNames = [...new Set(Object.values(newAlias))];
119
+ // console.log(` --- SCC: {${scc.join(",")}} ---`);
120
+
121
+ const sccCanonicalName = newNames.sort().join(":");
122
+ for (let relName in newAlias) {
123
+ if (newNames.length > 1)
124
+ relAlias[relName] = hash(newAlias[relName]+":"+sccCanonicalName);
125
+ else
126
+ relAlias[relName] = newAlias[relName];
127
+ // console.log(` ${relName} -> ${relAlias[relName]}`);
128
+ };
129
+ }
130
+
131
+ function exportRelation(rels, alias, name) {
132
+ const queue = [name];
133
+ const inQueue = { [name]: true };
134
+ const result = {};
135
+ const reName = (rel) => {
136
+ const newRel = {...rel};
137
+ switch (rel.op) {
138
+ case "product":
139
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: reName(exp)}));
140
+ break;
141
+ case "union":
142
+ newRel.union = rel.union.map(exp => reName(exp));
143
+ break;
144
+ case "comp":
145
+ newRel.comp = rel.comp.map(exp => reName(exp));
146
+ break;
147
+ case "vector":
148
+ newRel.vector = rel.vector.map(exp => reName(exp));
149
+ break;
150
+ case "caret":
151
+ newRel.caret = reName(rel.caret);
152
+ break;
153
+ case "ref": {
154
+ const n = rel.ref;
155
+ if (alias[n] == undefined) // built-in
156
+ return newRel;
157
+ newRel.ref = alias[n];
158
+ if (! inQueue[n]) {
159
+ inQueue[n] = true;
160
+ queue.push(n);
161
+ }
162
+ }; break;
163
+ default:
164
+ break;
165
+ };
166
+ return newRel;
167
+ };
168
+ while (queue.length > 0 ) {
169
+ const name = queue.shift();
170
+ const canonicalName = alias[name];
171
+ result[canonicalName] = {...rels[name], def: reName(rels[name].def), name: name};
172
+ }
173
+ return result;
174
+ }
175
+
176
+ export default { assignCanonicalNames, exportRelation };
177
+ 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.0",
4
4
  "description": "k-language for JSON-like data transformation",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
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
 
@@ -101,6 +102,7 @@ function patterns(representatives, rels) {
101
102
  // representatives:{"{}": "BG", ...}
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
@@ -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 };
package/pretty.mjs CHANGED
@@ -1,26 +1,22 @@
1
1
  import { find } from "./codes.mjs";
2
2
  import { TypePatternGraph } from "./typing.mjs";
3
3
 
4
- function compareAs(fn) {
5
- return function (a, b) {
6
- [a, b] = [a, b].map(fn);
7
- if (a < b) return -1;
8
- if (a > b) return 1;
9
- return 0;
10
- };
11
- }
12
-
13
4
  const nameRE = /^[a-zA-Z0-9_][a-zA-Z0-9_?!]*$/;
5
+ function pLabel_(label) {
6
+ return nameRE.test(label) ? ` ${label}` : `${JSON.stringify(label)}`;
7
+ }
8
+ function pLabel(label) {
9
+ return nameRE.test(label) ? `${label}` : `${JSON.stringify(label)}`;
10
+ }
14
11
 
15
12
  function prettyCode_labels (representatives, label_ref_map) {
16
13
  return Object.keys(label_ref_map)
17
- .sort( compareAs((x) => x) )
14
+ .sort()
18
15
  .map( (label) => {
19
- const plabel = nameRE.test(label) ? label : `'${label}'`;
20
16
  return `${prettyCode(representatives, {
21
17
  code: "ref",
22
18
  ref: label_ref_map[label],
23
- })} ${plabel}`;
19
+ })}${pLabel_(label)}`;
24
20
  })
25
21
  .join(", ");
26
22
  };
@@ -46,10 +42,10 @@ function prettyCode (representatives, codeExp) {
46
42
  }
47
43
  };
48
44
 
49
- function prettyFilter (prettyCode, filter) {
45
+ function prettyFilter ( filter ) {
50
46
  const fieldsStr = () => {
51
47
  const fields = Object.keys(filter.fields).map( (key) =>
52
- `${prettyFilter(prettyCode, filter.fields[key])} ${key}`
48
+ `${prettyFilter(filter.fields[key])}${pLabel_(key)}`
53
49
  );
54
50
  if (filter.open) fields.push("...");
55
51
  return fields.join(", ");
@@ -57,11 +53,11 @@ function prettyFilter (prettyCode, filter) {
57
53
  switch (filter.type) {
58
54
 
59
55
  case "code":
60
- return `$${prettyCode({code: "ref",ref: filter.code,})}`;
56
+ return `$${filter.code}`;
61
57
  case "name":
62
58
  return filter.name;
63
59
  case "vector":
64
- return `[${prettyFilter(prettyCode, filter.vector)}]${filter.name ? "=" + filter.name : ""}`;
60
+ return `[${prettyFilter(filter.vector)}]${filter.name ? "=" + filter.name : ""}`;
65
61
  case null:
66
62
  if (filter.name && Object.keys(filter.fields).length == 0)
67
63
  return filter.name;
@@ -74,12 +70,13 @@ function prettyFilter (prettyCode, filter) {
74
70
  throw new Error(`Unknown filter type ${filter.type} in ${JSON.stringify(filter)}`);
75
71
  }
76
72
 
77
- function prettyRel (prettyCode, exp) {
73
+ function prettyRel (exp) {
78
74
  "use strict";
79
75
  const pretty = (exp) => {
80
76
  switch (exp.op) {
81
77
  case "filter":
82
- return `?${prettyFilter(prettyCode, exp.filter)}`;
78
+ if (exp.filter.type == 'code') return `$${exp.filter.code}`;
79
+ return `?${prettyFilter(exp.filter)}`;
83
80
  case "pipe":
84
81
  return `|`;
85
82
  case "caret":
@@ -101,27 +98,16 @@ function prettyRel (prettyCode, exp) {
101
98
  case "dot":
102
99
  if ("number" === typeof exp.dot) {
103
100
  return `.${exp.dot}`;
104
- } else if (nameRE.test(exp.dot)) {
105
- return `.${exp.dot}`;
106
- } else {
107
- return `'${exp.dot}'`;
108
- }
109
- break;
101
+ } else
102
+ return `.${pLabel(exp.dot)}`;
110
103
  case "code":
111
- return `$${prettyCode({
112
- code: "ref",
113
- ref: exp.code,
114
- })}`;
104
+ return `$${exp.code}`;
115
105
  case "product":
116
106
  return (function (labelled) {
117
107
  return `{${labelled.join(", ")}}`;
118
108
  })(
119
109
  exp.product.map(function ({ label, exp }) {
120
- if (nameRE.test(label)) {
121
- return `${pretty(exp)} ${label}`;
122
- } else {
123
- return `${pretty(exp)} '${label}'`;
124
- }
110
+ return `${pretty(exp)}${pLabel_(label)}`;
125
111
  })
126
112
  );
127
113
  }
@@ -148,25 +134,24 @@ function patterns2filters(typePatternGraph, ...patternIds) {
148
134
 
149
135
  // variables - variables which will have to be added as extra filters
150
136
  const variables = inDegree.reduce( (variables, degree, i) => {
151
- if (degree > 1) variables[i] = "fresh";
137
+ if (degree > 1) variables[i] = true;
152
138
  return variables;
153
139
  }, {});
154
140
 
141
+ const filterVars = {};
155
142
  const buildFilter = (path, patternId, i) => {
156
143
  let named_filter = {};
157
144
  const pattern = newTypePatternGraph.get_pattern(patternId);
158
145
  if (variables[patternId] && pattern.pattern != 'type') {
159
- const name = `X${patternId}`;
160
- if (variables[patternId] == "done")
161
- return { type: 'name', name: name };
162
- variables[patternId] = "done";
163
- named_filter = { name: name };
146
+ if (filterVars[patternId])
147
+ return { type: 'name', name: filterVars[patternId] };
148
+ filterVars[patternId] = `X${Object.keys(filterVars).length}`;
149
+ named_filter = { name: filterVars[patternId] };
164
150
  }
165
151
 
166
-
167
152
  const edges = newTypePatternGraph.edges[patternId];
168
153
  const fields = () =>
169
- Object.keys(edges).reduce( (fields, key) =>{
154
+ Object.keys(edges).sort().reduce( (fields, key) =>{
170
155
  fields[key] = buildFilter([...path, key], Object.values(edges[key])[0], i);
171
156
  return fields;
172
157
  }, {});