@fraczak/k 5.0.0 → 5.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/BIG_PICTURE.md CHANGED
@@ -14,15 +14,18 @@ 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
31
  - by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n`
@@ -43,8 +46,34 @@ Some properties of these types:
43
46
  singleton union types in the syntax of the language. The other benefit is that we will be able to use the same syntax
44
47
  for writing out values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
45
48
  as a union type value (a variant).
46
- 2. The empty product type `{}` has a single value, called the _unit_ and also denoted by `{}`, which has no fields.
47
- 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
+ ```
48
77
 
49
78
  ### Partial functions
50
79
 
@@ -52,14 +81,17 @@ A _partial function_ is a function that is not defined for every value of its do
52
81
 
53
82
  There are three ways of combining partial functions:
54
83
 
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`,
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`,
56
86
  and `g` is defined on value `y` producing `z`, then `(f g)` is defined on value `x` producing `z`.
57
87
  2. __Union__: we write `< f1, f2 >` for a union of two functions `f1` and `f2`, meaning that
58
88
  if `f1` is defined on value `x` producing `y`, then `< f1, f2 >`
59
89
  is defined on value `x` producing `y`. Otherwise, i.e., when `f1` is not defined for `x`, `< f1 f2 >` acts as `f2`.
60
- 3. __Product__: we write `{f1 lab1, f2 lab2 }` for a product of two functions `f1` and `f2`, meaning that if `f1` and `f2`
61
- are both defined on value `x` producing `y1` and `y2`, respectively, then `{f1 lab1, f2 lab2 }`
62
- 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`.
63
95
 
64
96
  Actually, composition, union, and product combine any number of partial functions, not only two. For example,
65
97
  empty composition, `()`, defines the identity function.
@@ -72,41 +104,32 @@ which is the partial function the program defines.
72
104
  The language syntax is defined by:
73
105
 
74
106
  ```bnf
75
- program ::= (type_definition | function_definition) * expression
76
-
107
+ program ::= (type_definition | function_definition)* expression
77
108
  type_definition ::= '$' type_name '=' type ';'
78
- type ::= '{' type_label_list '}' | '<' type_label_list '>'
79
- type_label_list ::= /* empty */ | ( type_expression label_name ',' ) + type_expression label_name
80
- type_expression ::= type_name | type
81
-
82
- function_definition ::= function_name '=' expression ';'
83
- expression ::= projection | typing | filter | composition | union | product | function_name
84
- projection ::= '.' label_name
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
109
+ function_definition ::= name '=' expression ';'
110
+ expression ::= name | '$' type | '?' filter
111
+ | projection | composition | union | product
112
+ projection ::= '.' label
91
113
  composition ::= '(' expression * ')'
92
114
  union ::= '<' (expression ',') + expression '>'
93
- product ::= '{' expression_label_list '}'
94
- 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
95
118
  ```
96
119
 
97
- We assume that in a `kernel-core` program 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:
98
121
 
99
122
  1. All defined type names are distinct.
100
123
  2. All defined function names are distinct.
101
124
  3. All label names within a type definition or a `product` expression are locally distinct.
102
125
 
103
- The `typing` expression acts as a type annotation and can be seen as a 'filter', i.e., an identity
104
- 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.
105
128
 
106
129
  That's it.
107
130
 
108
131
  No `builtin` types, no `if` statement, no `loop`, no `throw`, no _closure_, no _annotations_, and no macros.
109
- Just types and partial functions.
132
+ Just _types_, _filters_, and _functions_.
110
133
 
111
134
  ### Normalization
112
135
 
@@ -126,20 +149,21 @@ a dictionary of _function definitions_, and the final _main expression_.
126
149
  In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
127
150
  the program on an input.
128
151
 
129
- Our objective however is to compute canonicals representations for types and normalized
152
+ Our objective however is to compute canonical representations for types and normalized
130
153
  representations for functions, so they could be reused.
131
154
 
132
155
  The normalization steps are:
133
156
 
134
157
  1. Build the _type graph_ from all types used by the program, i.e., used in _type definitions_
135
158
  as well as type expressions used in _function definitions_ and the _main expression_.
136
- 2. Annotate the expressions with types or type patterns; every node of the AST is annotated with a pair of type patterns.
137
- 3. Turn the _singleton_ type patterns into types and add them to the _type graph_. Go to (2) unless no change.
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.
138
161
 
162
+ ### Examples
139
163
 
140
- ### Example
164
+ #### Example 1
141
165
 
142
- ```k
166
+ ```k-repl
143
167
  $ bit = < {} o, {} i >;
144
168
  bit0 = {{} o} $bit;
145
169
  bit1 = {{} i} $bit;
@@ -199,6 +223,33 @@ We can write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
199
223
  The target type `${ byte byte, bit overflown }` was derived from pattern `?{ $byte byte, $bit overflown }`.
200
224
  Such a pattern is called _singleton pattern_, as only one type fits the pattern.
201
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
+ ```
252
+
202
253
  ## Universal Schema Registry
203
254
 
204
255
  Since the normalization process for types is fast and deterministic, we can build a universal
@@ -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/README.md CHANGED
@@ -54,7 +54,7 @@ the property doesn't exist. E.g.,
54
54
 
55
55
  There are three ways of combining functions:
56
56
 
57
- 1. **composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
57
+ 1. **Composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
58
58
  nested field.
59
59
 
60
60
  ```text
@@ -64,7 +64,7 @@ There are three ways of combining functions:
64
64
  {"other": {}} ... undefined
65
65
  ```
66
66
 
67
- 2. **merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
67
+ 2. **Merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
68
68
  `toto` if present; otherwise extracts `titi`.
69
69
 
70
70
  ```text
@@ -74,7 +74,7 @@ There are three ways of combining functions:
74
74
  {"x":{"o":{}}, "z": {"o":{}}} ... undefined
75
75
  ```
76
76
 
77
- 3. **product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
77
+ 3. **Product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
78
78
  .titi TITI}` extracts two fields and builds a record out of them.
79
79
 
80
80
  ```text
@@ -86,9 +86,9 @@ There are three ways of combining functions:
86
86
 
87
87
  **QUIZ**: What is:
88
88
 
89
- - empty composition: `()` ?
90
- - empty merge: `<>` ?
91
- - empty product: `{}` ?
89
+ - Empty composition: `()` ?
90
+ - Empty merge: `<>` ?
91
+ - Empty product: `{}` ?
92
92
  - `{{{{} s} s} s}` ?
93
93
  - `({{{() a} b} c} .c .b .a)` ?
94
94
 
@@ -96,9 +96,9 @@ There are three ways of combining functions:
96
96
 
97
97
  ### Syntactic sugar
98
98
 
99
- - parenthesis can be omitted, except for the empty composition `()`,
100
- - dot (`.`) in "projection" acts as a separator so the space around it
101
- can be omitted.
99
+ - Parenthesis can be omitted, except for the empty composition `()`,
100
+ - dot (`.`) in "projection" acts as a separator, so the surrounding
101
+ space can be omitted.
102
102
 
103
103
  For example, `(.toto .titi (.0 .1))` can be written as `.toto.titi.0.1`.
104
104
 
@@ -148,8 +148,8 @@ projection, via (`.`), to the canonical string representation of the value, e.g.
148
148
 
149
149
  #### Vector product
150
150
 
151
- Vector product can be seen as an abbreviation for product whose all fields
152
- have the same type, and the filed names are integers starting from zero.
151
+ The vector product can be seen as a shorthand for a product in which all fields
152
+ have the same type, and the field names are integers starting from zero.
153
153
  E.g., `{.toto 0, .titi 1, 123 2}` can be written as
154
154
  `[.toto, .titi, 123]`.
155
155
 
@@ -531,7 +531,7 @@ For example:
531
531
 
532
532
  ## k-REPL (Read-Evaluate-Print Loop)
533
533
 
534
- Also there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`),
534
+ Also, there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`),
535
535
  which acts like a toy shell for the language.
536
536
 
537
537
  ```k-repl
@@ -552,72 +552,74 @@ Very! experimental repl shell for 'k-language'...
552
552
 
553
553
  ## Using `k` from `javascript`
554
554
 
555
- import k from "@fraczak/k";
555
+ ```javascript
556
+ import k from "@fraczak/k";
556
557
 
557
- let k_expression = '()';
558
- k.run(k_expression,"ANYTHING...");
559
- // RETURNS: "ANYTHING..."
558
+ let k_expression = '()';
559
+ k.run(k_expression,"ANYTHING...");
560
+ // RETURNS: "ANYTHING..."
560
561
 
561
- k_expression = '{"ala" name, 23 age}';
562
- k.run(k_expression,"ANYTHING...");
563
- // RETURNS: {"name":"ala","age":23}
562
+ k_expression = '{"ala" name, 23 age}';
563
+ k.run(k_expression,"ANYTHING...");
564
+ // RETURNS: {"name":"ala","age":23}
564
565
 
565
- k_expression = '[.year, .age]';
566
- k.run(k_expression,{"year":2002,"age":19});
567
- // RETURNS: [2002,19]
566
+ k_expression = '[.year, .age]';
567
+ k.run(k_expression,{"year":2002,"age":19});
568
+ // RETURNS: [2002,19]
568
569
 
569
- k_expression = '[(), ()]';
570
- k.run(k_expression,"duplicate me");
571
- // RETURNS: ["duplicate me","duplicate me"]
570
+ k_expression = '[(), ()]';
571
+ k.run(k_expression,"duplicate me");
572
+ // RETURNS: ["duplicate me","duplicate me"]
572
573
 
573
- k_expression = '[[[()]]]';
574
- k.run(k_expression,"nesting");
575
- // RETURNS: [[["nesting"]]]
574
+ k_expression = '[[[()]]]';
575
+ k.run(k_expression,"nesting");
576
+ // RETURNS: [[["nesting"]]]
576
577
 
577
- k_expression = '[[()]] {() nested, .0.0 val}';
578
- k.run(k_expression,"nesting and accessing");
579
- // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
578
+ k_expression = '[[()]] {() nested, .0.0 val}';
579
+ k.run(k_expression,"nesting and accessing");
580
+ // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
580
581
 
581
- k_expression = '0000';
582
- k.run(k_expression,{"test":"parse integer"});
583
- // RETURNS: 0
582
+ k_expression = '0000';
583
+ k.run(k_expression,{"test":"parse integer"});
584
+ // RETURNS: 0
584
585
 
585
- k_expression = '[.y,.x] PLUS';
586
- k.run(k_expression,{"x":3,"y":4});
587
- // RETURNS: 7
586
+ k_expression = '[.y,.x] PLUS';
587
+ k.run(k_expression,{"x":3,"y":4});
588
+ // RETURNS: 7
588
589
 
589
- var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
590
+ var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
590
591
 
591
- k_fn({"age":23,"name":"Emily"});
592
- // RETURNS: {"nom":"Emily","age":23}
592
+ k_fn({"age":23,"name":"Emily"});
593
+ // RETURNS: {"nom":"Emily","age":23}
593
594
 
594
- k_fn({"age":16,"name":"Katrina"});
595
- // RETURNS: {"nom":"Katrina","age":"ado"}
595
+ k_fn({"age":16,"name":"Katrina"});
596
+ // RETURNS: {"nom":"Katrina","age":"ado"}
596
597
 
597
- k_fn({"age":2,"name":"Mark"});
598
- // RETURNS: {"nom":"Mark","age":"enfant"}
598
+ k_fn({"age":2,"name":"Mark"});
599
+ // RETURNS: {"nom":"Mark","age":"enfant"}
599
600
 
600
- k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
601
+ k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
601
602
 
602
- k_fn(1);
603
- // RETURNS: 1
603
+ k_fn(1);
604
+ // RETURNS: 1
604
605
 
605
- k_fn({"i":1});
606
- // RETURNS: {"i":1}
606
+ k_fn({"i":1});
607
+ // RETURNS: {"i":1}
607
608
 
608
- k_fn([{"i":2},{"i":3},{"t":[]}]);
609
- // RETURNS: undefined
609
+ k_fn([{"i":2},{"i":3},{"t":[]}]);
610
+ // RETURNS: undefined
610
611
 
611
- k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
612
- // RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
612
+ k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
613
+ // RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
613
614
 
614
- k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
615
+ k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
615
616
 
616
- k_fn({"None":"None"});
617
- // RETURNS: {"None":"None"}
617
+ k_fn({"None":"None"});
618
+ // RETURNS: {"None":"None"}
618
619
 
619
- k_fn({"list":{"ints":[]}});
620
- // RETURNS: {"list":{"ints":[]}}
620
+ k_fn({"list":{"ints":[]}});
621
+ // RETURNS: {"list":{"ints":[]}}
621
622
 
622
- k_fn({"list":{"ints":[1,2,3]}});
623
- // RETURNS: {"list":{"ints":[1,2,3]}}
623
+ k_fn({"list":{"ints":[1,2,3]}});
624
+ // RETURNS: {"list":{"ints":[1,2,3]}}
625
+ ```
package/export.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { prettyRel, patterns2filters } from "./pretty.mjs";
3
3
  import { hash } from "./hash.mjs";
4
4
 
5
- function simplifyRel(relDef) {
5
+ function simplifyRel(relDef,rels) {
6
6
  // remove filters and codes
7
7
  const prune = (rel) => {
8
8
  const newRel = {...rel};
@@ -32,6 +32,13 @@ function simplifyRel(relDef) {
32
32
  case 'code':
33
33
  case 'filter':
34
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;
35
42
  default:
36
43
  break;
37
44
  };
@@ -44,13 +51,13 @@ function simplifyRel(relDef) {
44
51
  const newRel = prune(rel);
45
52
  let resultRel = {...newRel};
46
53
  if (newRel.op == "identity")
47
- return filters[0];
54
+ return {...resultRel, ...filters[0]};
48
55
 
49
56
  if (newRel.op == "comp") {
50
57
  resultRel.comp = [ filters[0], ...newRel.comp, filters[1] ];
51
58
  return resultRel;
52
59
  }
53
- return {
60
+ return {...resultRel,
54
61
  op: "comp",
55
62
  comp: [filters[0], newRel, filters[1] ]
56
63
  };
@@ -108,11 +115,12 @@ function assignCanonicalNames(scc, rels, relAlias) {
108
115
  const relDef = rels[relName];
109
116
  if (relDef.def.op == "ref" && rels[relDef.def.ref] != undefined) {
110
117
  // inlining if direct alias to a non built-in relation
111
- relDef.simplified = simplifyRel(rels[relDef.def.ref]);
118
+ // relDef.def = rels[relDef.def.ref]; // simplifyRel(rels[relDef.def.ref],rels);
119
+ rels[relName] = rels[relDef.def.ref];
112
120
  } else {
113
- relDef.simplified = simplifyRel(relDef);
121
+ relDef.def = simplifyRel(relDef,rels);
114
122
  }
115
- newAlias[relName] = theID(relAlias, relDef.simplified, scc, relName);
123
+ newAlias[relName] = theID(relAlias, rels[relName].def, scc, relName);
116
124
  return newAlias;
117
125
  }, {});
118
126
  const newNames = [...new Set(Object.values(newAlias))];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
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
@@ -8,7 +8,7 @@ import { assignCanonicalNames } from "./export.mjs";
8
8
  const unitCode = codes.unitCode;
9
9
 
10
10
  function relDefToString(relDef) {
11
-
11
+ const start = new Date().getTime();
12
12
  const dumpPatterns = (rel) => {
13
13
  switch (rel.op) {
14
14
  case "product":
@@ -39,14 +39,20 @@ function relDefToString(relDef) {
39
39
  // console.log(relDef.typePatternGraph.edges);
40
40
  // console.log(relDef.def);
41
41
 
42
- return JSON.stringify(dumpPatterns(relDef.def).map((x) => {
42
+ const result = JSON.stringify(dumpPatterns(relDef.def).map((x) => {
43
43
  const { pattern, fields, type} = relDef.typePatternGraph.get_pattern(x);
44
44
  return {pattern, fields, type};
45
45
  }));
46
+
47
+ let took = new Date().getTime() - start;
48
+ if (took > 200) {
49
+ console.log(` -- RelDefToString: size=${result.length}, took ${took} msecs`);
50
+ }
51
+ return result;
46
52
  }
47
53
 
48
54
  function compactRel(relDef, name = "") {
49
- // console.log("********************************* compacting", name);
55
+ const start = new Date().getTime();
50
56
  const {def, typePatternGraph, varRefs } = relDef;
51
57
  // console.log("COMPACTING typePatternGraph of size", typePatternGraph.size());
52
58
 
@@ -92,14 +98,17 @@ function compactRel(relDef, name = "") {
92
98
  outputPatternId: renumbering[x.outputPatternId]
93
99
  }));;
94
100
 
95
- // console.log(`COMPACTED ${name}.typePatternGraph`, typePatternGraph.size(), '->', newTypePatternGraph.size());
101
+ const time = new Date().getTime() - start;
102
+ if (time > 500 ) {
103
+ console.log(`[${new Date().getTime() - start} msecs] - COMPACTED ${name}.typePatternGraph [${typePatternGraph.size()}->${newTypePatternGraph.size()}]`);
104
+ }
96
105
  return {def: newDef, typePatternGraph: newTypePatternGraph, varRefs: newVarRefs};
97
106
  }
98
107
 
99
108
  function patterns(representatives, rels) {
100
109
  // INPUT:
101
- // codes: {"BG": {"code": "product", "product": {}}, ...}
102
- // representatives:{"{}": "BG", ...}
110
+ // codes: {"KL": {"code": "product", "product": {}}, ...}
111
+ // representatives:{"{}": "KL", ...}
103
112
  // rels: {"rlz": [{op: comp,...}, ...], ...}
104
113
 
105
114
  const relAlias = {};
@@ -407,7 +416,7 @@ function patterns(representatives, rels) {
407
416
  rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
408
417
  break;
409
418
  default:
410
- throw new Error(`No definition found for ${rel.ref}`);
419
+ throw new Error(`No definition found for '${rel.ref}'`);
411
420
  }
412
421
  }
413
422
 
@@ -462,7 +471,7 @@ function patterns(representatives, rels) {
462
471
  // we will also generate the canonical representation of the relations in C and replace
463
472
  // the name to the relation by the hash of the canonical representation
464
473
 
465
- // console.log("----- SCC in order", sccInOrder);
474
+ // console.log("- SCC in order", sccInOrder);
466
475
 
467
476
  for (const scc of sccInOrder) {
468
477
 
@@ -470,7 +479,7 @@ function patterns(representatives, rels) {
470
479
 
471
480
  for (let iteration = 1; iteration <= maxNumberOfIterations; iteration++) {
472
481
 
473
- // console.log(`>>>>>> Iteration ${iteration} for SCC: { ${DAGnodes[scc].scc} }`);
482
+ // console.log(`-- Iteration ${iteration}/${maxNumberOfIterations} for SCC: { ${DAGnodes[scc].scc} }`);
474
483
  // 4.1 For every r in C, compute the new typePatternGraph of r, i.e.:
475
484
 
476
485
  let before = JSON.stringify(DAGnodes[scc].scc.map( relName => {
@@ -481,11 +490,11 @@ function patterns(representatives, rels) {
481
490
  // console.log(relDefToString(rels[relName]));
482
491
  return relDefToString(rels[relName]);
483
492
  }));
484
-
493
+
485
494
  let after = JSON.stringify(DAGnodes[scc].scc.map( relName => {
486
495
  const relDef = compactRel(rels[relName],relName);
487
496
  for (let i = 0; i < relDef.varRefs.length; i++) {
488
- // console.log(` ####### Processing ${relName}(..x${i}=${relDef.varRefs[i].varName}..)`);
497
+ const start = new Date().getTime();
489
498
  let varRel = relDef.varRefs[i];
490
499
  let { varName } = varRel;
491
500
  try {
@@ -493,7 +502,6 @@ function patterns(representatives, rels) {
493
502
  let varInputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[0]);
494
503
  let varOutputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[1]);
495
504
  let cloned = varRootDef.typePatternGraph.clone([varInputPatternId, varOutputPatternId], relDef.typePatternGraph);
496
-
497
505
  relDef.typePatternGraph.unify(`ref:input ${relName}(${varName})`, varRel.inputPatternId, cloned[varInputPatternId]);
498
506
  relDef.typePatternGraph.unify("ref:output", varRel.outputPatternId, cloned[varOutputPatternId]);
499
507
  } catch (e) {
@@ -502,6 +510,10 @@ function patterns(representatives, rels) {
502
510
  - ${e.message}`;
503
511
  throw e;
504
512
  }
513
+ const tookMs = new Date().getTime() - start;
514
+ if (tookMs > 200) {
515
+ console.log(` ---- Processing ${relName}(.., x${i}=${relDef.varRefs[i].varName}, ..) took: ${tookMs} ms`);
516
+ }
505
517
  }
506
518
  rels[relName] = compactRel(relDef,relName);
507
519
  return relDefToString(rels[relName]);
package/repl.mjs CHANGED
@@ -51,8 +51,8 @@ rl.on('line', (line) => {
51
51
  process.exit(0);
52
52
  });
53
53
 
54
- let old_val = {};
55
- let val = {};
54
+ let old_val = k.compile("{}")({});
55
+ let val = old_val;
56
56
  const printVal = function (v = val) {
57
57
  if (v === undefined)
58
58
  console.log("...",v);
@@ -140,6 +140,9 @@ function evaluate(line) {
140
140
  codeName = codeName.slice(1);
141
141
  const canonicalName = run.defs.representatives[codeName] || codeName;
142
142
  const codeExp = find( canonicalName );
143
+ if (codeExp.def == "builtin") {
144
+ return console.log(` $ ${canonicalName} = :builtin;`);
145
+ }
143
146
  console.log(` $ ${canonicalName} = ${prettyCode(run.defs.representatives, codeExp)}; -- ${codeExp.def}`);
144
147
  // --pp
145
148
  } else if (line.match(/^[ \n\t]*(?:--pp)?$/)) {
@@ -187,7 +190,7 @@ function evaluate(line) {
187
190
  console.log({
188
191
  name: canonicalRelName,
189
192
  type: `${filtersStr[0]} --> ${filtersStr[1]}`,
190
- def: prettyRel(rel.simplified)
193
+ def: prettyRel(rel.def)
191
194
  });
192
195
  if (DEBUG_FLAG) {
193
196
  val = exportRelation(run.defs.rels, run.defs.relAlias, relName);
package/typing.mjs CHANGED
@@ -3,20 +3,36 @@ import codes from "./codes.mjs";
3
3
 
4
4
  const unitCode = codes.unitCode;
5
5
 
6
- function asMapSet(vector) {
7
- return vector.reduce( (mapSet, x) => ({...mapSet, [JSON.stringify(x)]: x}), {})
6
+ function asSet(vector) {
7
+ return Array.from(new Set(vector));
8
8
  };
9
9
 
10
10
  function setUnion(...vectors) {
11
- return Object.values(asMapSet([].concat(...vectors)));
11
+ const unionSet = new Set();
12
+ for (const v of vectors) {
13
+ for (const element of v) {
14
+ unionSet.add(element);
15
+ }
16
+ }
17
+ return Array.from(unionSet);
12
18
  }
13
19
 
14
20
  function subsetP(v1,v2) {
15
- const v2Set = asMapSet(v2);
16
- return Object.keys(asMapSet(v1)).every(x => x in v2Set);
21
+ const v2Set = new Set(v2);
22
+ return v1.every(x => v2Set.has(x));
17
23
  }
24
+
18
25
  function eqsetP(v1,v2) {
19
- return subsetP(v1,v2) && subsetP(v2,v1);
26
+ const v1Set = new Set(v1);
27
+ const v2Set = new Set(v2);
28
+ if (v1Set.size != v2Set.size) return false;
29
+
30
+ for (const value of v1Set) {
31
+ if (!v2Set.has(value)) {
32
+ return false;
33
+ }
34
+ }
35
+ return true;
20
36
  }
21
37
 
22
38
  const matchAllPattern = {pattern: '(...)', fields: []};
@@ -76,7 +92,7 @@ class TypePatternForest {
76
92
  // const node = {...flatTypePattern, _id: id};
77
93
  const node = {...flatTypePattern}
78
94
  that.nodes.push(node);
79
- Object.values(asMapSet(children.map(that.find.bind(that))))
95
+ Array.from(new Set(children.map(that.find.bind(that))))
80
96
  .forEach(rep => {that.parent[rep] = id});
81
97
  return id;
82
98
  }
@@ -139,7 +155,6 @@ class TypePatternGraph {
139
155
  for( const id of singletonPatterns) {
140
156
  this.unify('singleton', id, this.getTypeId(representatives[`-${id}-`]));
141
157
  }
142
-
143
158
  }
144
159
 
145
160
 
@@ -165,27 +180,29 @@ class TypePatternGraph {
165
180
  )
166
181
  );
167
182
 
168
- const gEdgesWithoutParrallelEdges = Object.values(asMapSet(gEdges));
169
-
183
+ const gEdgesWithoutParrallelEdges = Array.from(new Set(gEdges));
170
184
  const gGraph = new Graph(gEdgesWithoutParrallelEdges);
171
185
  const queue = gNodes.filter(x =>
172
186
  this.patterns.nodes[x].pattern in {'()':1, '(...)':1,'{...}':1, '<...>':1});
173
- const excluded = asMapSet(queue);
187
+ const excludedSet = new Set(queue);
188
+
174
189
  while (queue.length > 0) {
175
190
  const x = queue.pop();
176
191
  for (const e of gGraph.dst[x] || []) {
177
192
  const y = gGraph.edges[e].src;
178
- if (y in excluded) continue;
179
- excluded[y] = y;
193
+ if (excludedSet.has(y)) continue;
194
+ excludedSet.add(y);
180
195
  queue.push(y);
181
196
  }
182
197
  }
183
- return gNodes.filter(x => !((x in excluded) || (this.patterns.nodes[x].pattern == 'type')));
198
+
199
+ return gNodes.filter(x => !((excludedSet.has(x)) || (this.patterns.nodes[x].pattern == 'type')));
184
200
  }
185
201
 
186
202
  getCompressed() {
187
203
  const newTypePatternGraph = new TypePatternGraph();
188
204
  const renamed = this.cloneAll(newTypePatternGraph);
205
+
189
206
  newTypePatternGraph.turnSingletonPatternsIntoCodes();
190
207
 
191
208
  Object.keys(renamed).forEach( id => {
@@ -223,7 +240,9 @@ class TypePatternGraph {
223
240
 
224
241
  // console.log("equivalence", equivalence);
225
242
 
243
+ let count = 0;
226
244
  const areDifferent = (i1,i2) => {
245
+ count++;
227
246
  const p1 = newTypePatternGraph.get_pattern(i1);
228
247
  const p2 = newTypePatternGraph.get_pattern(i2);
229
248
  if (p1.pattern != p2.pattern) return true;
@@ -239,7 +258,9 @@ class TypePatternGraph {
239
258
  return false;
240
259
  }
241
260
  let changed = true;
261
+ let count2 = 0;
242
262
  while (changed) {
263
+ count2++;
243
264
  changed = false;
244
265
  const equivalenceSize = equivalence.length;
245
266
  for (let eqClassPos = 0; eqClassPos < equivalenceSize; eqClassPos++) {
@@ -285,7 +306,7 @@ class TypePatternGraph {
285
306
  for (const oldId in newIds) {
286
307
  const newId = newIds[oldId];
287
308
  const pattern = newTypePatternGraph.get_pattern(oldId);
288
- const newPattern = compressedTypePatternGraph.get_pattern(newId);
309
+ // const newPattern = compressedTypePatternGraph.get_pattern(newId);
289
310
  // console.log("patterns", pattern, newPattern);
290
311
  if (pattern.pattern == 'type')
291
312
  continue;
@@ -294,7 +315,7 @@ class TypePatternGraph {
294
315
  compressedTypePatternGraph.edges[newId] = {};
295
316
  for (const lab in edges) {
296
317
  // console.log("edges[lab]", edges[lab]);
297
- compressedTypePatternGraph.edges[newId][lab] = asMapSet(Object.values(edges[lab]).map(x =>
318
+ compressedTypePatternGraph.edges[newId][lab] = asSet(edges[lab].map(x =>
298
319
  newIds[reps[newTypePatternGraph.find(x)][0]]));
299
320
  }
300
321
  }
@@ -308,6 +329,7 @@ class TypePatternGraph {
308
329
  // console.log("nodes", compressedTypePatternGraph.patterns.nodes);
309
330
  // console.log("edges", compressedTypePatternGraph.edges);
310
331
  // console.log("--------------------")
332
+
311
333
  return {typePatternGraph: compressedTypePatternGraph, remapping};
312
334
  }
313
335
 
@@ -343,7 +365,7 @@ class TypePatternGraph {
343
365
  const new_id = result[i];
344
366
  targetGraph.edges[new_id] = {};
345
367
  for (const lab in edges) {
346
- targetGraph.edges[new_id][lab] = asMapSet(Object.values(edges[lab]).map(x => result[find(x)]));
368
+ targetGraph.edges[new_id][lab] = asSet(edges[lab].map(x => result[find(x)]));
347
369
  }
348
370
  }
349
371
  return result;
@@ -584,7 +606,7 @@ class TypePatternGraph {
584
606
 
585
607
  unify(rule, ...ids) {
586
608
  const find = (x) => this.find(x);
587
- const reps = Object.values(asMapSet(ids.map(find)));
609
+ const reps = Array.from(new Set(ids.map(find)));
588
610
  if (reps.length < 2) return false;
589
611
  const rep_patters = reps.map(x => {
590
612
  const pattern = this.get_pattern(x);
@@ -615,8 +637,8 @@ class TypePatternGraph {
615
637
  for (const lab in edges) {
616
638
  // if the pattern is [] all labels are treated as 'vector-member'
617
639
  const new_lab = new_pattern.pattern == '[]' ? 'vector-member' : lab;
618
- const dests = asMapSet(Object.values(edges[lab]).map(find));
619
- this.edges[new_id][new_lab] = {...this.edges[new_id][new_lab], ...dests};
640
+ const dests = asSet(edges[lab].map(find));
641
+ this.edges[new_id][new_lab] = asSet([...(this.edges[new_id][new_lab] || []), ...dests]);
620
642
  }
621
643
  }
622
644
  // add stuff for types
@@ -629,7 +651,7 @@ class TypePatternGraph {
629
651
  // all edges are goin to unit type
630
652
  const unit_id = this.getTypeId(unitCode);
631
653
  for (const lab in this.edges[new_id]) {
632
- this.edges[new_id][lab][unit_id] = unit_id;
654
+ this.edges[new_id][lab] = asSet([unit_id]);
633
655
  }
634
656
  }; break;
635
657
  case 'product':
@@ -637,7 +659,7 @@ class TypePatternGraph {
637
659
  const type_fields = Object.keys(code[code.code]);
638
660
  for (const lab of type_fields) {
639
661
  const target_type_id = this.getTypeId(code[code.code][lab]);
640
- this.edges[new_id][lab]= {...this.edges[new_id][lab], [target_type_id]: target_type_id};
662
+ this.edges[new_id][lab]= asSet([...(this.edges[new_id][lab] ||[]), target_type_id]);
641
663
  }
642
664
  }; break;
643
665
  case 'vector': {
@@ -645,18 +667,13 @@ class TypePatternGraph {
645
667
  const target_type_id = this.getTypeId(code.vector);
646
668
 
647
669
  this.edges[new_id] = {
648
- 'vector-member': {
649
- [target_type_id]: target_type_id,
650
- ...asMapSet(
651
- setUnion(...Object.values(this.edges[new_id]).map(aSet => Object.values(aSet)))
652
- )
653
- }
670
+ 'vector-member': asSet([ target_type_id, ...Object.values(this.edges[new_id])])
654
671
  }
655
672
  }
656
673
  }
657
674
  }
658
675
  for ( const lab of Object.keys(this.edges[new_id]) ) {
659
- this.unify(rule+'.', ...Object.values(this.edges[new_id][lab]));
676
+ this.unify(rule+'.', ...this.edges[new_id][lab]);
660
677
  }
661
678
 
662
679
  return true;
@@ -667,7 +684,7 @@ class TypePatternGraph {
667
684
  const find = (x) => this.patterns.find(x);
668
685
  this.edges[id] = {};
669
686
  for (const [lab,dest] of Object.entries(fields)) {
670
- this.edges[id][lab] = asMapSet(dest.map(find));
687
+ this.edges[id][lab] = asSet(dest.map(find));
671
688
  }
672
689
  return id;
673
690
  }