@fraczak/k 1.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/README.md +181 -0
- package/index.coffee +18 -0
- package/index.js +40 -0
- package/k.coffee +45 -0
- package/k.js +72 -0
- package/k.sh +73 -0
- package/my-objects.json +6 -0
- package/package.json +22 -0
- package/parser.jison +93 -0
- package/parser.js +703 -0
- package/run.coffee +76 -0
- package/run.js +157 -0
- package/symbol-table.coffee +14 -0
- package/symbol-table.js +42 -0
- package/test.coffee +34 -0
- package/test.js +76 -0
- package/test.t +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# k-language
|
|
2
|
+
|
|
3
|
+
Another JSON-to-JSON transformation notation. A `k`-expression
|
|
4
|
+
(script) defines a __partial function__, i.e., a function which, in
|
|
5
|
+
general, is not defined for every input. `k`-expressions can be
|
|
6
|
+
combined to build other `k`-expressions in three ways:
|
|
7
|
+
|
|
8
|
+
1. composition, e.g.:
|
|
9
|
+
|
|
10
|
+
(E1 E2 E3 ...)
|
|
11
|
+
|
|
12
|
+
Apart from empty composition, `()`, which defines _identity_, the paranthesis can be omitted.
|
|
13
|
+
|
|
14
|
+
2. product, e.g., vector:
|
|
15
|
+
|
|
16
|
+
[ E1, E2, E3, ... ]
|
|
17
|
+
|
|
18
|
+
or structure:
|
|
19
|
+
|
|
20
|
+
{ E1 "e1", E2 "e2", E3 "e3", ... }
|
|
21
|
+
|
|
22
|
+
3. union (merge), e.g.:
|
|
23
|
+
|
|
24
|
+
< E1, E2, E3, ... >
|
|
25
|
+
|
|
26
|
+
Elementary partial functions are:
|
|
27
|
+
|
|
28
|
+
1. _projection_, i.e., extracting the value of a given field or
|
|
29
|
+
index. Examples:
|
|
30
|
+
|
|
31
|
+
.x
|
|
32
|
+
."field name"
|
|
33
|
+
.4
|
|
34
|
+
|
|
35
|
+
The function is defined only if its argument is a structure with
|
|
36
|
+
the field (or a vector with the index).
|
|
37
|
+
|
|
38
|
+
2. _constants_, literals for Strings, Booleans, and
|
|
39
|
+
Integers. Examples:
|
|
40
|
+
|
|
41
|
+
"a string"
|
|
42
|
+
'another "String"'
|
|
43
|
+
123
|
|
44
|
+
false
|
|
45
|
+
|
|
46
|
+
Those functions are always defined, i.e., they ignore their argument.
|
|
47
|
+
|
|
48
|
+
3. There is a number of built-in functions: `GT`, `EQ`, `PLUS`,
|
|
49
|
+
`TIMES`, `MINUS`, `DIV`, `CONCAT`, `toJSON`, `fromJSON`. For example
|
|
50
|
+
|
|
51
|
+
[1, 2, 3] PLUS --> integer constant 6
|
|
52
|
+
[4, 4] TIMES toJSON --> string constant "16"
|
|
53
|
+
[3, 2] GT --> vector constant [3, 2]
|
|
54
|
+
[3, 4] GT --> is not defined!
|
|
55
|
+
|
|
56
|
+
A more interesting example could be:
|
|
57
|
+
|
|
58
|
+
< [.0, .1] GT, [.1, .0] GT > .0
|
|
59
|
+
|
|
60
|
+
which selects the bigger of two first integers of a vector argument, i.e.,
|
|
61
|
+
|
|
62
|
+
[3,8] < [.0, .1] GT, [.1, .0] GT > .0 --> 8
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Command line script
|
|
67
|
+
|
|
68
|
+
There is a wrapper, `./node_modules/.bin/k` , which makes it easy to
|
|
69
|
+
run the language from command line.
|
|
70
|
+
|
|
71
|
+
$ ./node_modules/.bin/k
|
|
72
|
+
... errors ...
|
|
73
|
+
Usage: ./node_modules/.bin/k ( k-expr | -k k-file) [ json-file ]
|
|
74
|
+
E.g., cat '{"a": 10}' | ./node_modules/.bin/k '[(),()]'
|
|
75
|
+
|
|
76
|
+
### Examples
|
|
77
|
+
|
|
78
|
+
1. One `k`-expression with one `json`-object:
|
|
79
|
+
|
|
80
|
+
$ echo '{"x": 12, "y": 13}' | ./k.coffee '{ <.x, "no x"> x, () input}'
|
|
81
|
+
{"x":12,"input":{"x":12,"y":13}}
|
|
82
|
+
|
|
83
|
+
2. By providing only `k`-expression, the script will compile the
|
|
84
|
+
`k`-expression and apply the generated function to the `stdin`, line by line:
|
|
85
|
+
|
|
86
|
+
$ ./k.coffee '<["x=",.x," & y=",.y],["only x=",.x],["only y=",.y],["no x nor y"]>{CONCAT "x&y"}'
|
|
87
|
+
|
|
88
|
+
{"y": 123, "x": 432,"others": "..."} --> {"x&y":"x=432 & y=123"}
|
|
89
|
+
{"x":987} --> {"x&y":"only x=987"}
|
|
90
|
+
{"z":123} --> {"x&y":"no x nor y"}
|
|
91
|
+
^D - to interrupt
|
|
92
|
+
|
|
93
|
+
3. If the `k`-expression is long, it can be put in a file, e.g.:
|
|
94
|
+
|
|
95
|
+
$ cat test.k
|
|
96
|
+
--------- comments start by #, --, or // ----------------------------------
|
|
97
|
+
< -- merge of 4 partial functions...
|
|
98
|
+
["x=", .x, " & y=", .y], -- produces a vector of 4 values, if fields 'x' and 'y' are present
|
|
99
|
+
["only x=", .x], -- produces a pair '["only x=", "value-of-x"]', for input like {"x":"value-of-x"}
|
|
100
|
+
-- it is defined only if field 'x' is present
|
|
101
|
+
["only y=", .y],
|
|
102
|
+
["no x nor y"] -- defined for all input, returns always the same one element vector
|
|
103
|
+
>
|
|
104
|
+
-- one of the string vectors is passed to the following partial function,
|
|
105
|
+
-- which produces a record (map) with one field "x&y", whose value is the
|
|
106
|
+
-- result of concatenating elements of the passed in vector
|
|
107
|
+
{ CONCAT "x&y" }
|
|
108
|
+
------------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
We can use it by:
|
|
111
|
+
|
|
112
|
+
$ ./k.coffee -k test.k
|
|
113
|
+
|
|
114
|
+
If we want to read `json` objects from a file, e.g., `my-objects.json`, we do
|
|
115
|
+
|
|
116
|
+
$ ./k.coffee -k test.k -j my-objects.json
|
|
117
|
+
{"x&y":"x=432 & y=123"}
|
|
118
|
+
{"x&y":"onlyx=987"}
|
|
119
|
+
{"x&y":"no x nor y"}
|
|
120
|
+
|
|
121
|
+
where:
|
|
122
|
+
|
|
123
|
+
$ cat my-objects.json
|
|
124
|
+
####################################################
|
|
125
|
+
# empty lines and lines starting with # are ignored
|
|
126
|
+
{"y": 123, "x": 432,"others": "..."}
|
|
127
|
+
{"x":987}
|
|
128
|
+
{"z":123}
|
|
129
|
+
####################################################
|
|
130
|
+
|
|
131
|
+
# Using from `javascript`
|
|
132
|
+
|
|
133
|
+
var k = require("k");
|
|
134
|
+
|
|
135
|
+
var k_expression;
|
|
136
|
+
|
|
137
|
+
k_expression = '()';
|
|
138
|
+
k.run(t_expression,"ANYTHING...");
|
|
139
|
+
// RETURNS: "ANYTHING..."
|
|
140
|
+
|
|
141
|
+
k_expression = '{"ala" name, 23 age}';
|
|
142
|
+
k.run(k_expression,"ANYTHING...");
|
|
143
|
+
// RETURNS: {"name":"ala","age":23}
|
|
144
|
+
|
|
145
|
+
k_expression = '[.year, .age]';
|
|
146
|
+
k.run(k_expression,{"year":2002,"age":19});
|
|
147
|
+
// RETURNS: [2002,19]
|
|
148
|
+
|
|
149
|
+
k_expression = '[(), ()]';
|
|
150
|
+
k.run(k_expression,"duplicate me");
|
|
151
|
+
// RETURNS: ["duplicate me","duplicate me"]
|
|
152
|
+
|
|
153
|
+
k_expression = '[[[()]]]';
|
|
154
|
+
k.run(k_expression,"nesting");
|
|
155
|
+
// RETURNS: [[["nesting"]]]
|
|
156
|
+
|
|
157
|
+
k_expression = '[[()]] {() nested, .0.0 val}';
|
|
158
|
+
k.run(k_expression,"nesting and accessing")
|
|
159
|
+
// RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
|
|
160
|
+
|
|
161
|
+
k_expression = '0000';
|
|
162
|
+
k.run(k_expression,{"test":"parse integer"});
|
|
163
|
+
// RETURNS: 0
|
|
164
|
+
|
|
165
|
+
k_expression = '[.y,.x] PLUS';
|
|
166
|
+
k.run(k_expression,{"x":3,"y":4});
|
|
167
|
+
// RETURNS: 7
|
|
168
|
+
|
|
169
|
+
k_expression = '{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}';
|
|
170
|
+
|
|
171
|
+
var k_fn = k.compile(k_expression);
|
|
172
|
+
|
|
173
|
+
k_fn({"age":23,"name":"Emily"});
|
|
174
|
+
// RETURNS: {"nom":"Emily","age":23}
|
|
175
|
+
|
|
176
|
+
k_fn({"age":16,"name":"Katrina"});
|
|
177
|
+
// RETURNS: {"nom":"Katrina","age":"ado"}
|
|
178
|
+
|
|
179
|
+
k_fn({"age":2,"name":"Mark"})
|
|
180
|
+
// RETURNS: {"nom":"Mark","age":"enfant"}
|
|
181
|
+
|
package/index.coffee
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
fs = require "fs"
|
|
2
|
+
{parse} = require "./parser.js"
|
|
3
|
+
run = require "./run"
|
|
4
|
+
|
|
5
|
+
compile = (script) ->
|
|
6
|
+
do (exp = parse script) ->
|
|
7
|
+
(data) -> run exp, data
|
|
8
|
+
compile.doc = "Transforms k-script (string) into a function"
|
|
9
|
+
|
|
10
|
+
compile_file = (fileName) ->
|
|
11
|
+
compile fs.readFileSync(fileName).toString "utf8"
|
|
12
|
+
compile_file.doc = "Transforms k-script (from file) into a function"
|
|
13
|
+
|
|
14
|
+
runScriptOnData = (script, data) ->
|
|
15
|
+
compile(script) data
|
|
16
|
+
runScriptOnData.doc = "Run 'script' (string) on 'data': (script,data) -> data"
|
|
17
|
+
|
|
18
|
+
module.exports = {compile, compile_file, run: runScriptOnData, parse: parse}
|
package/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Generated by CoffeeScript 2.7.0
|
|
2
|
+
(function() {
|
|
3
|
+
var compile, compile_file, fs, parse, run, runScriptOnData;
|
|
4
|
+
|
|
5
|
+
fs = require("fs");
|
|
6
|
+
|
|
7
|
+
({parse} = require("./parser.js"));
|
|
8
|
+
|
|
9
|
+
run = require("./run");
|
|
10
|
+
|
|
11
|
+
compile = function(script) {
|
|
12
|
+
return (function(exp) {
|
|
13
|
+
return function(data) {
|
|
14
|
+
return run(exp, data);
|
|
15
|
+
};
|
|
16
|
+
})(parse(script));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
compile.doc = "Transforms k-script (string) into a function";
|
|
20
|
+
|
|
21
|
+
compile_file = function(fileName) {
|
|
22
|
+
return compile(fs.readFileSync(fileName).toString("utf8"));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
compile_file.doc = "Transforms k-script (from file) into a function";
|
|
26
|
+
|
|
27
|
+
runScriptOnData = function(script, data) {
|
|
28
|
+
return compile(script)(data);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
runScriptOnData.doc = "Run 'script' (string) on 'data': (script,data) -> data";
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
compile,
|
|
35
|
+
compile_file,
|
|
36
|
+
run: runScriptOnData,
|
|
37
|
+
parse: parse
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
}).call(this);
|
package/k.coffee
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env coffee
|
|
2
|
+
fs = require "fs"
|
|
3
|
+
k = require "./"
|
|
4
|
+
|
|
5
|
+
prog = process.argv[1]
|
|
6
|
+
|
|
7
|
+
{kScript,jsonStream} = do (args = process.argv.slice(2)) ->
|
|
8
|
+
try
|
|
9
|
+
kScript = do (arg = args.shift()) ->
|
|
10
|
+
throw new Error() unless arg?
|
|
11
|
+
if arg is "-k"
|
|
12
|
+
fs.readFileSync(args.shift()).toString "utf8"
|
|
13
|
+
else
|
|
14
|
+
arg
|
|
15
|
+
|
|
16
|
+
kScript = k.compile kScript
|
|
17
|
+
|
|
18
|
+
jsonStream = do (arg = args.shift()) ->
|
|
19
|
+
return process.stdin unless arg?
|
|
20
|
+
fs.createReadStream args.shift()
|
|
21
|
+
|
|
22
|
+
{kScript, jsonStream}
|
|
23
|
+
catch e
|
|
24
|
+
console.error e
|
|
25
|
+
console.error "Usage: #{prog} ( k-expr | -k k-file ) [ json-file ]"
|
|
26
|
+
console.error " E.g., echo '{\"a\": 12}' | #{prog} '[(),()]'"
|
|
27
|
+
process.exit -1
|
|
28
|
+
|
|
29
|
+
do (buffer = [], line = 0) ->
|
|
30
|
+
jsonStream.on 'data', (data) ->
|
|
31
|
+
[first, rest...] = data.toString("utf8").split "\n"
|
|
32
|
+
buffer.push first
|
|
33
|
+
if rest.length > 0
|
|
34
|
+
todo = buffer.join ""
|
|
35
|
+
[rest..., last] = rest
|
|
36
|
+
buffer = [last]
|
|
37
|
+
todo = [todo, rest...]
|
|
38
|
+
for json in todo
|
|
39
|
+
if not json.match /^[ \n\t]*(?:#.*)?$/
|
|
40
|
+
try
|
|
41
|
+
console.log JSON.stringify kScript JSON.parse json
|
|
42
|
+
catch e
|
|
43
|
+
console.error "Problem [line #{line}]: '#{json}'"
|
|
44
|
+
console.error e
|
|
45
|
+
line = line + 1
|
package/k.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Generated by CoffeeScript 2.7.0
|
|
2
|
+
(function() {
|
|
3
|
+
//!/usr/bin/env coffee
|
|
4
|
+
var fs, jsonStream, k, kScript, prog,
|
|
5
|
+
splice = [].splice;
|
|
6
|
+
|
|
7
|
+
fs = require("fs");
|
|
8
|
+
|
|
9
|
+
k = require("./");
|
|
10
|
+
|
|
11
|
+
prog = process.argv[1];
|
|
12
|
+
|
|
13
|
+
({kScript, jsonStream} = (function(args) {
|
|
14
|
+
var e;
|
|
15
|
+
try {
|
|
16
|
+
kScript = (function(arg) {
|
|
17
|
+
if (arg == null) {
|
|
18
|
+
throw new Error();
|
|
19
|
+
}
|
|
20
|
+
if (arg === "-k") {
|
|
21
|
+
return fs.readFileSync(args.shift()).toString("utf8");
|
|
22
|
+
} else {
|
|
23
|
+
return arg;
|
|
24
|
+
}
|
|
25
|
+
})(args.shift());
|
|
26
|
+
kScript = k.compile(kScript);
|
|
27
|
+
jsonStream = (function(arg) {
|
|
28
|
+
if (arg == null) {
|
|
29
|
+
return process.stdin;
|
|
30
|
+
}
|
|
31
|
+
return fs.createReadStream(args.shift());
|
|
32
|
+
})(args.shift());
|
|
33
|
+
return {kScript, jsonStream};
|
|
34
|
+
} catch (error) {
|
|
35
|
+
e = error;
|
|
36
|
+
console.error(e);
|
|
37
|
+
console.error(`Usage: ${prog} ( k-expr | -k k-file ) [ json-file ]`);
|
|
38
|
+
console.error(` E.g., echo '{\"a\": 12}' | ${prog} '[(),()]'`);
|
|
39
|
+
return process.exit(-1);
|
|
40
|
+
}
|
|
41
|
+
})(process.argv.slice(2)));
|
|
42
|
+
|
|
43
|
+
(function(buffer, line) {
|
|
44
|
+
return jsonStream.on('data', function(data) {
|
|
45
|
+
var e, first, i, json, last, len, ref, rest, results, todo;
|
|
46
|
+
[first, ...rest] = data.toString("utf8").split("\n");
|
|
47
|
+
buffer.push(first);
|
|
48
|
+
if (rest.length > 0) {
|
|
49
|
+
todo = buffer.join("");
|
|
50
|
+
ref = rest, [...rest] = ref, [last] = splice.call(rest, -1);
|
|
51
|
+
buffer = [last];
|
|
52
|
+
todo = [todo, ...rest];
|
|
53
|
+
results = [];
|
|
54
|
+
for (i = 0, len = todo.length; i < len; i++) {
|
|
55
|
+
json = todo[i];
|
|
56
|
+
if (!json.match(/^[ \n\t]*(?:#.*)?$/)) {
|
|
57
|
+
try {
|
|
58
|
+
console.log(JSON.stringify(kScript(JSON.parse(json))));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
e = error;
|
|
61
|
+
console.error(`Problem [line ${line}]: '${json}'`);
|
|
62
|
+
console.error(e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
results.push(line = line + 1);
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
})([], 0);
|
|
71
|
+
|
|
72
|
+
}).call(this);
|
package/k.sh
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generated by CoffeeScript 2.7.0
|
|
3
|
+
(function() {
|
|
4
|
+
//!/usr/bin/env coffee
|
|
5
|
+
var fs, jsonStream, k, kScript, prog,
|
|
6
|
+
splice = [].splice;
|
|
7
|
+
|
|
8
|
+
fs = require("fs");
|
|
9
|
+
|
|
10
|
+
k = require("./");
|
|
11
|
+
|
|
12
|
+
prog = process.argv[1];
|
|
13
|
+
|
|
14
|
+
({kScript, jsonStream} = (function(args) {
|
|
15
|
+
var e;
|
|
16
|
+
try {
|
|
17
|
+
kScript = (function(arg) {
|
|
18
|
+
if (arg == null) {
|
|
19
|
+
throw new Error();
|
|
20
|
+
}
|
|
21
|
+
if (arg === "-k") {
|
|
22
|
+
return fs.readFileSync(args.shift()).toString("utf8");
|
|
23
|
+
} else {
|
|
24
|
+
return arg;
|
|
25
|
+
}
|
|
26
|
+
})(args.shift());
|
|
27
|
+
kScript = k.compile(kScript);
|
|
28
|
+
jsonStream = (function(arg) {
|
|
29
|
+
if (arg == null) {
|
|
30
|
+
return process.stdin;
|
|
31
|
+
}
|
|
32
|
+
return fs.createReadStream(args.shift());
|
|
33
|
+
})(args.shift());
|
|
34
|
+
return {kScript, jsonStream};
|
|
35
|
+
} catch (error) {
|
|
36
|
+
e = error;
|
|
37
|
+
console.error(e);
|
|
38
|
+
console.error(`Usage: ${prog} ( k-expr | -k k-file ) [ json-file ]`);
|
|
39
|
+
console.error(` E.g., echo '{\"a\": 12}' | ${prog} '[(),()]'`);
|
|
40
|
+
return process.exit(-1);
|
|
41
|
+
}
|
|
42
|
+
})(process.argv.slice(2)));
|
|
43
|
+
|
|
44
|
+
(function(buffer, line) {
|
|
45
|
+
return jsonStream.on('data', function(data) {
|
|
46
|
+
var e, first, i, json, last, len, ref, rest, results, todo;
|
|
47
|
+
[first, ...rest] = data.toString("utf8").split("\n");
|
|
48
|
+
buffer.push(first);
|
|
49
|
+
if (rest.length > 0) {
|
|
50
|
+
todo = buffer.join("");
|
|
51
|
+
ref = rest, [...rest] = ref, [last] = splice.call(rest, -1);
|
|
52
|
+
buffer = [last];
|
|
53
|
+
todo = [todo, ...rest];
|
|
54
|
+
results = [];
|
|
55
|
+
for (i = 0, len = todo.length; i < len; i++) {
|
|
56
|
+
json = todo[i];
|
|
57
|
+
if (!json.match(/^[ \n\t]*(?:#.*)?$/)) {
|
|
58
|
+
try {
|
|
59
|
+
console.log(JSON.stringify(kScript(JSON.parse(json))));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
e = error;
|
|
62
|
+
console.error(`Problem [line ${line}]: '${json}'`);
|
|
63
|
+
console.error(e);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
results.push(line = line + 1);
|
|
67
|
+
}
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
})([], 0);
|
|
72
|
+
|
|
73
|
+
}).call(this);
|
package/my-objects.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fraczak/k",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "k-language for untyped JSON like data transformation",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"prepare": "coffee -c symbol-table.coffee; jison parser.jison",
|
|
8
|
+
"prepublishOnly": "coffee -c *.coffee; echo '#!/usr/bin/env node' | cat - k.js > k.sh",
|
|
9
|
+
"test": "coffee -c symbol-table.coffee; jison parser.jison; coffee test.coffee;"
|
|
10
|
+
},
|
|
11
|
+
"author": "Wojciech Fraczak",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"bin": {
|
|
14
|
+
"k": "./k.sh"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"jison": "^0.4.18"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"coffeescript": "^2.7.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/parser.jison
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
%{
|
|
2
|
+
|
|
3
|
+
var s = require("./symbol-table");
|
|
4
|
+
|
|
5
|
+
%}
|
|
6
|
+
|
|
7
|
+
%lex
|
|
8
|
+
|
|
9
|
+
%%
|
|
10
|
+
[/][*]([*][^/]|[^*])*[*][/] /* c-comment */
|
|
11
|
+
("//"|"#"|"%"|"--")[^\n]* /* one line comment */
|
|
12
|
+
\s+ /* blanks */
|
|
13
|
+
"<" return 'LA';
|
|
14
|
+
"{" return 'LC';
|
|
15
|
+
"[" return 'LB';
|
|
16
|
+
"(" return 'LP';
|
|
17
|
+
">" return 'RA';
|
|
18
|
+
"}" return 'RC';
|
|
19
|
+
"]" return 'RB';
|
|
20
|
+
")" return 'RP';
|
|
21
|
+
"." return 'DOT';
|
|
22
|
+
"," return 'COMMA';
|
|
23
|
+
\"[^\"\n]*\"|\'[^\'\n]*\' return 'STRING'
|
|
24
|
+
[a-zA-Z_][a-zA-Z0-9_]* return 'NAME';
|
|
25
|
+
0|[-]?[1-9][0-9]* return 'INT';
|
|
26
|
+
<<EOF>> return 'EOF';
|
|
27
|
+
|
|
28
|
+
/lex
|
|
29
|
+
|
|
30
|
+
%token NAME STRING INT
|
|
31
|
+
%token LA LC LB LP RA RP RB RC COMMA
|
|
32
|
+
%token EOF
|
|
33
|
+
|
|
34
|
+
%start input_with_eof
|
|
35
|
+
|
|
36
|
+
%%
|
|
37
|
+
|
|
38
|
+
name: NAME { $$ = String(yytext); };
|
|
39
|
+
str: STRING { $$ = String(yytext).slice(1,-1); };
|
|
40
|
+
int: INT { $$ = parseInt(String(yytext)); };
|
|
41
|
+
|
|
42
|
+
input_with_eof: comp EOF {
|
|
43
|
+
// console.log(JSON.stringify($1, "", 2));
|
|
44
|
+
return $1;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
comp
|
|
48
|
+
: exp { $$ = $1; }
|
|
49
|
+
| comp exp { $$ = s.comp($1, $2); }
|
|
50
|
+
;
|
|
51
|
+
|
|
52
|
+
exp
|
|
53
|
+
: LC labelled RC { $$ = $2; }
|
|
54
|
+
| LB list RB { $$ = {op: "vector", vector: $2}; }
|
|
55
|
+
| LA list RA { $$ = {op: "union", union: $2}; }
|
|
56
|
+
| name { $$ = {op: "ref", ref: $1}; }
|
|
57
|
+
| LP RP { $$ = s.identity; }
|
|
58
|
+
| LP comp RP { $$ = $2; }
|
|
59
|
+
| str { $$ = {op: "str", str: $1}; }
|
|
60
|
+
| int { $$ = {op: "int", int: $1}; }
|
|
61
|
+
| DOT int { $$ = {op: "dot", dot: $2}; }
|
|
62
|
+
| DOT str { $$ = {op: "dot", dot: $2}; }
|
|
63
|
+
| DOT name { $$ = {op: "dot", dot: $2}; }
|
|
64
|
+
;
|
|
65
|
+
|
|
66
|
+
labelled
|
|
67
|
+
: { $$ = {op: "product", product: []}; }
|
|
68
|
+
| non_empty_labelled { $$ = $1; }
|
|
69
|
+
;
|
|
70
|
+
|
|
71
|
+
non_empty_labelled
|
|
72
|
+
: comp_label
|
|
73
|
+
{ $$ = {op: "product", product: [$1]}; }
|
|
74
|
+
| non_empty_labelled COMMA comp_label
|
|
75
|
+
{ $1.product = [].concat($1.product,$3); $$ = $1; }
|
|
76
|
+
;
|
|
77
|
+
|
|
78
|
+
comp_label
|
|
79
|
+
: comp name { $$ = {label: $2, exp: $1}; }
|
|
80
|
+
| comp str { $$ = {label: $2, exp: $1}; }
|
|
81
|
+
;
|
|
82
|
+
|
|
83
|
+
list
|
|
84
|
+
: /*empty */ { $$ = []; }
|
|
85
|
+
| non_empty_list { $$ = $1; }
|
|
86
|
+
;
|
|
87
|
+
|
|
88
|
+
non_empty_list
|
|
89
|
+
: comp { $$ = [$1]; }
|
|
90
|
+
| non_empty_list COMMA comp { $$ = [].concat($1,$3); }
|
|
91
|
+
;
|
|
92
|
+
|
|
93
|
+
%%
|