@fraczak/k 5.1.2 → 6.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/.codex +0 -0
- package/Code-derivation-tests/blowup.mjs +3 -3
- package/Code-derivation-tests/bnat.mjs +2 -2
- package/Code-derivation-tests/codec-witness-pattern.mjs +50 -0
- package/Code-derivation-tests/convergence-strategy.mjs +24 -0
- package/Code-derivation-tests/dot.mjs +12 -9
- package/Code-derivation-tests/filter-syntax.mjs +21 -0
- package/Code-derivation-tests/loop.mjs +8 -2
- package/Code-derivation-tests/nat.mjs +1 -1
- package/Code-derivation-tests/not-not.mjs +3 -3
- package/Code-derivation-tests/pattern-envelope.mjs +68 -0
- package/Code-derivation-tests/rlz.mjs +2 -2
- package/{BIG_PICTURE.md → DOCS/BIG_PICTURE.md} +103 -50
- package/DOCS/REPL.md +191 -0
- package/DOCS/a.html +398 -0
- package/DOCS/book/01-introduction.md +51 -0
- package/DOCS/book/02-syntax-and-values.md +111 -0
- package/DOCS/book/03-types-as-finite-automata.md +156 -0
- package/DOCS/book/04-partial-functions-and-composition.md +146 -0
- package/DOCS/book/05-typing-filters-and-normalization.md +111 -0
- package/DOCS/book-combined.html +3798 -0
- package/DOCS/book-combined.md +3745 -0
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +5682 -0
- package/DOCS/book.html +65 -0
- package/DOCS/book.md +54 -0
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +150 -0
- package/NAT.k~ +452 -0
- package/README.md +204 -508
- package/TypeGraph.mjs +43 -0
- package/TypePatternGraph.mjs +251 -0
- package/Value.mjs +192 -0
- package/augmentation.mjs +198 -0
- package/codecs/BINARY_FORMAT.html +471 -0
- package/codecs/BINARY_FORMAT.md +53 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +303 -0
- package/codecs/README.md +215 -0
- package/codecs/example-pipeline.mjs +149 -0
- package/codecs/ieee.mjs +140 -0
- package/codecs/int.mjs +104 -0
- package/codecs/json-codec.mjs +215 -0
- package/codecs/json.mjs +40 -0
- package/codecs/k-parse.mjs +90 -0
- package/codecs/k-pattern.mjs +75 -0
- package/codecs/k-print.mjs +46 -0
- package/codecs/runtime/codec.mjs +768 -0
- package/codecs/runtime/ieee-pattern.mjs +30 -0
- package/codecs/runtime/pattern-json.mjs +96 -0
- package/codecs/runtime/prefix-codec.mjs +425 -0
- package/codecs/runtime/show-value.mjs +68 -0
- package/codecs/runtime/typeFromValue.mjs +34 -0
- package/codecs/runtime/unicode-string.mjs +220 -0
- package/codecs/show.mjs +41 -0
- package/codecs/string-codec.mjs +80 -0
- package/codecs/unit.mjs +51 -0
- package/codecs/utf16.mjs +66 -0
- package/codecs/utf8.mjs +37 -0
- package/codes.mjs +146 -44
- package/compiler.mjs +111 -0
- package/convergence.mjs +197 -0
- package/core.k +197 -0
- package/export.mjs +22 -32
- package/hash.mjs +29 -31
- package/ieee.klib +0 -0
- package/index.mjs +41 -15
- package/k-vscode/.vscode/launch.json +14 -0
- package/k-vscode/README.md +65 -0
- package/k-vscode/language-configuration.json +42 -0
- package/k-vscode/package-lock.json +4176 -0
- package/k-vscode/package.json +57 -0
- package/k-vscode/snippets/k.json +92 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +243 -0
- package/k.mjs +56 -54
- package/object.mjs +517 -0
- package/objects/README.md +38 -0
- package/objects/compile-lib.mjs +42 -0
- package/objects/compile.mjs +42 -0
- package/objects/decompile.mjs +32 -0
- package/package.json +13 -6
- package/parser.jison +27 -46
- package/parser.mjs +68 -100
- package/pretty.mjs +21 -40
- package/repl2.mjs +662 -0
- package/run.mjs +264 -231
- package/symbol-table.mjs +3 -17
- package/test-fingerprint.mjs +7 -7
- package/test-hash-fuzz.mjs +107 -0
- package/test-hash-normalization.mjs +134 -0
- package/test-k-object.mjs +74 -0
- package/test-repl2.mjs +106 -0
- package/test.mjs +45 -70
- package/tests.sh +10 -1
- package/type_registry/registry.mjs~ +105 -0
- package/type_registry/typeParser.jison~ +132 -0
- package/typing.mjs +129 -676
- package/unification.mjs +157 -0
- package/valueIO.mjs +54 -0
- package/valueParser.jison +143 -0
- package/valueParser.mjs +713 -0
- package/Code-derivation-tests/built-in.mjs +0 -19
- package/Code-derivation-tests/vector.mjs +0 -19
- package/Filters/bug.k +0 -3
- package/Filters/composing.k +0 -15
- package/Filters/filter.k +0 -9
- package/TODO.md +0 -53
- package/TYPING.md +0 -365
- package/k-repl.cast +0 -1913
- package/k.cast +0 -618
- package/my-objects.json +0 -6
- package/patterns.mjs +0 -543
- package/repl.mjs +0 -213
- package/test-filters.mjs +0 -45
- package/typing-test.mjs +0 -40
package/unification.mjs
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
function subsetP(v1,v2) {
|
|
2
|
+
const v2Set = new Set(v2);
|
|
3
|
+
return v1.every(x => v2Set.has(x));
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function eqsetP(v1,v2) {
|
|
7
|
+
const v1Set = new Set(v1);
|
|
8
|
+
const v2Set = new Set(v2);
|
|
9
|
+
if (v1Set.size != v2Set.size) return false;
|
|
10
|
+
|
|
11
|
+
for (const value of v1Set) {
|
|
12
|
+
if (!v2Set.has(value)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function setUnion(...vectors) {
|
|
20
|
+
const unionSet = new Set();
|
|
21
|
+
for (const v of vectors) {
|
|
22
|
+
for (const element of v) {
|
|
23
|
+
unionSet.add(element);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return Array.from(unionSet);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function unify_two_patterns(findCode, p1, p2) {
|
|
30
|
+
// console.log(`unify_two_patterns(${JSON.stringify(p1)}, ${JSON.stringify(p2)})`);
|
|
31
|
+
if (p1.pattern === '()' || p2.pattern === '()') {
|
|
32
|
+
throw new Error("Closed unknown pattern '()' is not supported. Use '{}' or '<>' to choose product or union.");
|
|
33
|
+
}
|
|
34
|
+
if ((p1.pattern === '(...)' && (p1.fields || []).length !== 0) || (p2.pattern === '(...)' && (p2.fields || []).length !== 0)) {
|
|
35
|
+
throw new Error("Unknown-kind pattern '(...)' cannot have fields.");
|
|
36
|
+
}
|
|
37
|
+
switch (p1.pattern) {
|
|
38
|
+
case '(...)':
|
|
39
|
+
switch (p2.pattern) {
|
|
40
|
+
case '(...)':
|
|
41
|
+
return {...p2, fields: setUnion(p1.fields, p2.fields)};
|
|
42
|
+
case '{...}':
|
|
43
|
+
return {...p2, fields: setUnion(p1.fields, p2.fields)};
|
|
44
|
+
case '<...>':
|
|
45
|
+
return {...p2, fields: setUnion(p1.fields, p2.fields)};
|
|
46
|
+
case '{}':
|
|
47
|
+
if (subsetP(p1.fields, p2.fields)) return p2;
|
|
48
|
+
throw new Error(`Cannot unify (${p1.fields},...) with {${p2.fields}}`);
|
|
49
|
+
case '<>':
|
|
50
|
+
if (subsetP(p1.fields, p2.fields)) return p2;
|
|
51
|
+
throw new Error(`Cannot unify (${p1.fields},...) with <${p2.fields}>`);
|
|
52
|
+
case 'type': {
|
|
53
|
+
const code = findCode(p2.type);
|
|
54
|
+
// console.log(JSON.stringify({p1,p2,code}));
|
|
55
|
+
switch (code.code) {
|
|
56
|
+
case 'product':
|
|
57
|
+
case 'union': {
|
|
58
|
+
let p2_fields = Object.keys(code[code.code]);
|
|
59
|
+
if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
|
|
60
|
+
}; break;
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
|
|
63
|
+
}
|
|
64
|
+
} ;
|
|
65
|
+
break;
|
|
66
|
+
case '{...}':
|
|
67
|
+
switch (p2.pattern) {
|
|
68
|
+
case '{...}':
|
|
69
|
+
return {...p2, fields: setUnion(p1.fields, p2.fields)};
|
|
70
|
+
case '<...>':
|
|
71
|
+
throw new Error('Cannot unify {...} with <...>');
|
|
72
|
+
case '{}':
|
|
73
|
+
if (subsetP(p1.fields, p2.fields)) return p2;
|
|
74
|
+
throw new Error(`Cannot unify {${p1.fields},...} with {${p2.fields}}`);
|
|
75
|
+
case '<>':
|
|
76
|
+
throw new Error('Cannot unify {...} with <>');
|
|
77
|
+
case 'type': {
|
|
78
|
+
const code = findCode(p2.type);
|
|
79
|
+
switch (code.code) {
|
|
80
|
+
case 'product':{
|
|
81
|
+
let p2_fields = Object.keys(code[code.code]);
|
|
82
|
+
if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
break;
|
|
89
|
+
case '<...>':
|
|
90
|
+
switch (p2.pattern) {
|
|
91
|
+
case '<...>':
|
|
92
|
+
return {pattern: '<...>', fields: setUnion(p1.fields, p2.fields)};
|
|
93
|
+
case '{}':
|
|
94
|
+
throw new Error('Cannot unify <...> with {}');
|
|
95
|
+
case '<>':
|
|
96
|
+
if (subsetP(p1.fields, p2.fields)) return p2;
|
|
97
|
+
throw new Error(`Cannot unify <${p1.fields},...> with <${p2.fields}>`);
|
|
98
|
+
case 'type':{
|
|
99
|
+
const code = findCode(p2.type);
|
|
100
|
+
switch (code.code) {
|
|
101
|
+
|
|
102
|
+
case 'union': {
|
|
103
|
+
let p2_fields = Object.keys(code[code.code]);
|
|
104
|
+
if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
break;
|
|
111
|
+
case '{}':
|
|
112
|
+
switch (p2.pattern) {
|
|
113
|
+
case '{}':
|
|
114
|
+
if (eqsetP(p1.fields, p2.fields)) return p2;
|
|
115
|
+
throw new Error(`Cannot unify {${p1.fields}} with {${p2.fields}}`);
|
|
116
|
+
case '<>':
|
|
117
|
+
throw new Error('Cannot unify {} with <>');
|
|
118
|
+
case 'type': {
|
|
119
|
+
const code = findCode(p2.type);
|
|
120
|
+
switch (code.code) {
|
|
121
|
+
case 'product':{
|
|
122
|
+
let p2_fields = Object.keys(code[code.code]);
|
|
123
|
+
if (eqsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
break;
|
|
130
|
+
case '<>':
|
|
131
|
+
switch (p2.pattern) {
|
|
132
|
+
case '<>':
|
|
133
|
+
if (eqsetP(p1.fields, p2.fields)) return p2;
|
|
134
|
+
throw new Error(`Cannot unify <${p1.fields}> with <${p2.fields}>`);
|
|
135
|
+
case 'type': {
|
|
136
|
+
const code = findCode(p2.type);
|
|
137
|
+
switch (code.code) {
|
|
138
|
+
case 'union':{
|
|
139
|
+
let p2_fields = Object.keys(code[code.code]);
|
|
140
|
+
if (eqsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
break;
|
|
147
|
+
case 'type':
|
|
148
|
+
switch (p2.pattern) {
|
|
149
|
+
case 'type':
|
|
150
|
+
if (p1.type != p2.type)
|
|
151
|
+
throw new Error(`Cannot unify different types: ${p1.type}:${findCode(p1.type).def} and ${p2.type}:${findCode(p2.type).def}`);
|
|
152
|
+
return p2;
|
|
153
|
+
};
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
return unify_two_patterns(findCode, p2, p1);
|
|
157
|
+
}
|
package/valueIO.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value I/O with Type Information
|
|
3
|
+
*
|
|
4
|
+
* This module wraps the valueParser to support explicit type information.
|
|
5
|
+
*
|
|
6
|
+
* Migration strategy (2 steps):
|
|
7
|
+
* - Step 1 (current): Add type parameter to all parseValue calls, but keep text parsing
|
|
8
|
+
* - Step 2 (future): Replace implementation to use binary codec
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { parse as parseValueText } from "./valueParser.mjs";
|
|
12
|
+
import { Value, fromObject } from "./Value.mjs";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse a value from text with optional type information
|
|
16
|
+
*
|
|
17
|
+
* @param {string} text - JSON-like text representation
|
|
18
|
+
* @param {string|null} typeName - Canonical type name (e.g., "@ABC123...")
|
|
19
|
+
* @param {Object|null} typeInfo - Type definition (for validation/future use)
|
|
20
|
+
* @returns {Value} - Product or Variant
|
|
21
|
+
*
|
|
22
|
+
* Step 1: Type parameters are accepted but not yet used
|
|
23
|
+
* Step 2: Will validate against type and eventually parse binary format
|
|
24
|
+
*/
|
|
25
|
+
export function parseValue(text, typeName = null, typeInfo = null) {
|
|
26
|
+
// Step 1: Ignore type for now, just parse text as before
|
|
27
|
+
// TODO Step 2: Validate parsed value against typeInfo if provided
|
|
28
|
+
return parseValueText(text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Serialize a value to text
|
|
33
|
+
*
|
|
34
|
+
* @param {Value} value - Product or Variant
|
|
35
|
+
* @param {string|null} typeName - Canonical type name (optional)
|
|
36
|
+
* @param {Object|null} typeInfo - Type definition (optional)
|
|
37
|
+
* @returns {string} - JSON representation
|
|
38
|
+
*
|
|
39
|
+
* Step 1: Returns JSON, ignoring type
|
|
40
|
+
* Step 2: Will encode to binary format
|
|
41
|
+
*/
|
|
42
|
+
export function printValue(value, typeName = null, typeInfo = null) {
|
|
43
|
+
// Step 1: Just serialize to JSON as before
|
|
44
|
+
// TODO Step 2: Encode to binary format using codec
|
|
45
|
+
return JSON.stringify(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convert JavaScript object to k Value
|
|
50
|
+
* (No change needed - this is type-agnostic)
|
|
51
|
+
*/
|
|
52
|
+
export { fromObject } from "./Value.mjs";
|
|
53
|
+
|
|
54
|
+
export default { parseValue, printValue, fromObject };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
%{
|
|
2
|
+
|
|
3
|
+
import { Product, Variant } from "./Value.mjs";
|
|
4
|
+
|
|
5
|
+
//--------------------------
|
|
6
|
+
|
|
7
|
+
function anError(pos,msg) {
|
|
8
|
+
throw new Error(`Parse Error (lines ${pos.first_line}:${pos.first_column}...${pos.last_line}:${pos.last_column}): ${msg}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getToken(yytext,yy,lstack) {
|
|
12
|
+
const yylloc = yy.lexer.yylloc;
|
|
13
|
+
const loc = lstack[lstack.length - 1]; // { first_line: 5, last_line: 5, first_column: 2, last_column: 3 };
|
|
14
|
+
const start = {line: loc.first_line, column: loc.first_column + 1};
|
|
15
|
+
const end = {line: start.line, column: start.column + yytext.length};
|
|
16
|
+
const result = {start,end,value: String(yytext)};
|
|
17
|
+
// console.log(result);
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fromEscString(escString) {
|
|
22
|
+
const isSingleQuoted = escString.startsWith("'");
|
|
23
|
+
let str = escString.substring(1, escString.length - 1);
|
|
24
|
+
if (isSingleQuoted) {
|
|
25
|
+
str = str.replace(/\\'/g, "'");
|
|
26
|
+
} else {
|
|
27
|
+
str = str.replace(/\\"/g, '"');
|
|
28
|
+
}
|
|
29
|
+
str = str.replace(/\\\\/g, '\\');
|
|
30
|
+
str = str.replace(/\\\//g, '/');
|
|
31
|
+
str = str.replace(/\\b/g, '\b');
|
|
32
|
+
str = str.replace(/\\f/g, '\f');
|
|
33
|
+
str = str.replace(/\\n/g, '\n');
|
|
34
|
+
str = str.replace(/\\r/g, '\r');
|
|
35
|
+
str = str.replace(/\\t/g, '\t');
|
|
36
|
+
str = str.replace(/\\u([\dA-F]{4})/gi, (match, p1) => String.fromCharCode(parseInt(p1, 16)));
|
|
37
|
+
|
|
38
|
+
return str;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
%}
|
|
43
|
+
|
|
44
|
+
%lex
|
|
45
|
+
|
|
46
|
+
%%
|
|
47
|
+
[/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/] /* c-comment */
|
|
48
|
+
("//"|"#"|"%"|"--"|"\\")[^\n]* /* one line comment */
|
|
49
|
+
\s+ /* blanks */
|
|
50
|
+
"{" return 'LC';
|
|
51
|
+
"}" return 'RC';
|
|
52
|
+
"[" return 'LB';
|
|
53
|
+
"]" return 'RB';
|
|
54
|
+
"," return 'COMMA';
|
|
55
|
+
":" return 'COL';
|
|
56
|
+
\"([^"\\]|\\(.|\n))*\"|\'([^'\\]|\\(.|\n))*\' return 'STRING';
|
|
57
|
+
[a-zA-Z0-9_][a-zA-Z0-9_?!]* return 'NAME';
|
|
58
|
+
<<EOF>> return 'EOF';
|
|
59
|
+
|
|
60
|
+
/lex
|
|
61
|
+
|
|
62
|
+
%token NAME STRING
|
|
63
|
+
%token LC RC LB RB COMMA COL
|
|
64
|
+
%token EOF
|
|
65
|
+
|
|
66
|
+
%start input_with_eof
|
|
67
|
+
|
|
68
|
+
%%
|
|
69
|
+
|
|
70
|
+
str : STRING { $$ = getToken(yytext,yy,_$); $$.value = fromEscString($$.value);}
|
|
71
|
+
| NAME { $$ = getToken(yytext,yy,_$); }
|
|
72
|
+
;
|
|
73
|
+
lc: LC { $$ = getToken(yytext,yy,_$); };
|
|
74
|
+
rc: RC { $$ = getToken(yytext,yy,_$); };
|
|
75
|
+
lb: LB { $$ = getToken(yytext,yy,_$); };
|
|
76
|
+
rb: RB { $$ = getToken(yytext,yy,_$); };
|
|
77
|
+
comma: COMMA { $$ = getToken(yytext,yy,_$); };
|
|
78
|
+
col: COL { $$ = getToken(yytext,yy,_$); };
|
|
79
|
+
|
|
80
|
+
input_with_eof
|
|
81
|
+
: exp EOF {
|
|
82
|
+
// console.log(`Parsed: ${$1.value.toString()}`);
|
|
83
|
+
return $1.value;
|
|
84
|
+
}
|
|
85
|
+
;
|
|
86
|
+
|
|
87
|
+
exp
|
|
88
|
+
: lc labelled rc { $$ = {value: $2.value, start: $1.start, end: $3.end}; }
|
|
89
|
+
| lb exps rb { $$ = {value: $2.value, start: $1.start, end: $3.end}; }
|
|
90
|
+
| str { $$ = {value: new Variant($1.value, new Product({})), start: $1.start, end: $1.end}; }
|
|
91
|
+
| name { $$ = {value: new Variant($1.value, new Product({})), start: $1.start, end: $1.end}; }
|
|
92
|
+
;
|
|
93
|
+
|
|
94
|
+
exps
|
|
95
|
+
: /* empty */ { $$ = {value: new Product({})}; }
|
|
96
|
+
| non_empty_exps {
|
|
97
|
+
if ($1.list.length === 1) {
|
|
98
|
+
$$ = {value: new Variant('0', $1.list[0].value)};
|
|
99
|
+
} else {
|
|
100
|
+
const product = {};
|
|
101
|
+
$1.list.forEach( (e, i) => product[i] = e.value );
|
|
102
|
+
$$ = {value: new Product(product)};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
;
|
|
106
|
+
|
|
107
|
+
non_empty_exps
|
|
108
|
+
: exp { $$ = {list: [$1]} ; }
|
|
109
|
+
| non_empty_exps comma exp
|
|
110
|
+
{ $$ = {list: [].concat($1.list,$3)}; }
|
|
111
|
+
;
|
|
112
|
+
|
|
113
|
+
labelled
|
|
114
|
+
: /* empty */ { $$ = {value: new Product({})}; }
|
|
115
|
+
| non_empty_labelled {
|
|
116
|
+
if ($1.list.length === 1) {
|
|
117
|
+
$$ = {value: new Variant($1.list[0].label, $1.list[0].value)};
|
|
118
|
+
} else {
|
|
119
|
+
const product = {};
|
|
120
|
+
for (const {label, value} of $1.list) {
|
|
121
|
+
if (product.hasOwnProperty(label))
|
|
122
|
+
anError(@0,`Duplicate label '${label}'.`);
|
|
123
|
+
product[label] = value;
|
|
124
|
+
}
|
|
125
|
+
$$ = {value: new Product(product)};
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
;
|
|
129
|
+
|
|
130
|
+
non_empty_labelled
|
|
131
|
+
: exp_label
|
|
132
|
+
{ $$ = {list: [$1]} ; }
|
|
133
|
+
| non_empty_labelled comma exp_label
|
|
134
|
+
{ $$ = {list: [].concat($1.list,$3)}; }
|
|
135
|
+
;
|
|
136
|
+
|
|
137
|
+
exp_label
|
|
138
|
+
: exp str { $$ = {label: $2.value, value: $1.value}; }
|
|
139
|
+
| str col exp { $$ = {label: $1.value, value: $3.value}; }
|
|
140
|
+
| bits col exp { $$ = {label: $1.value, value: $3.value}; }
|
|
141
|
+
;
|
|
142
|
+
|
|
143
|
+
%%
|