@fraczak/k 5.0.0 → 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,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/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.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
@@ -98,8 +98,8 @@ function compactRel(relDef, name = "") {
98
98
 
99
99
  function patterns(representatives, rels) {
100
100
  // INPUT:
101
- // codes: {"BG": {"code": "product", "product": {}}, ...}
102
- // representatives:{"{}": "BG", ...}
101
+ // codes: {"KL": {"code": "product", "product": {}}, ...}
102
+ // representatives:{"{}": "KL", ...}
103
103
  // rels: {"rlz": [{op: comp,...}, ...], ...}
104
104
 
105
105
  const relAlias = {};
@@ -407,7 +407,7 @@ function patterns(representatives, rels) {
407
407
  rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
408
408
  break;
409
409
  default:
410
- throw new Error(`No definition found for ${rel.ref}`);
410
+ throw new Error(`No definition found for '${rel.ref}'`);
411
411
  }
412
412
  }
413
413
 
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);