@fraczak/k 2.1.1 → 2.1.4

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.
@@ -0,0 +1,18 @@
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+
8
+ {
9
+ "type": "node",
10
+ "request": "launch",
11
+ "name": "Launch Program",
12
+ "skipFiles": [
13
+ "<node_internals>/**"
14
+ ],
15
+ "program": "${workspaceFolder}/repl.mjs"
16
+ }
17
+ ]
18
+ }
package/README.md CHANGED
@@ -185,6 +185,7 @@ can be written as `[.toto, .titi, 123]`.
185
185
 
186
186
  _Codes_ (prefixed by `$`) can be defined by taged union and product. E.g.:
187
187
 
188
+
188
189
  $nat = <nat 1, {} 0>;
189
190
  $pair = {nat x, nat y};
190
191
 
@@ -217,7 +218,7 @@ i.e., turn into a regular vector using `CARET` (`^`) operator. E.g.:
217
218
  [1,2,3] --> []
218
219
  ```
219
220
 
220
- The PIPE operator can be used, e.g., to define a Cartesian product:
221
+ The PIPE operator can be used for defining the Cartesian product:
221
222
 
222
223
  ```k
223
224
  [.0 |, .1 |] ^ :
@@ -241,13 +242,11 @@ times value `x` appears in the list. (see `Examples/list-comprehension.k` for a
241
242
  $ int;
242
243
  ```
243
244
 
244
- **WARNING**: The `CARET` operator makes composition not associative, i.e., if you use
245
- nested _list comprehension_ you cannot drop paranthesis anymore.
246
- E.g., `| (| PLUS ^ .0) ^` and `| | PLUS ^ .0 ^` are two different functions:
245
+ **WARNING**: When using `CARET` operator paranthesis may be required, e.g., like in:
247
246
 
248
247
  ```k
249
- { |(| PLUS ^ .0) ^ x, | | PLUS ^ .0 ^ y }:
250
- [[[1,2],[3,4]],[[5,6],[7,8]]] --> {"x":[3,11],"y":[3,7]}
248
+ | ( [|,|] ^ ) ^ :
249
+ [[1,2],[3,4]] --> [[[1,1],[1,2],[2,1],[2,2]],[[3,3],[3,4],[4,3],[4,4]]]
251
250
  ```
252
251
 
253
252
  ---
package/README.md.orig ADDED
@@ -0,0 +1,527 @@
1
+ # k-language
2
+
3
+ npm install '@fraczak/k'
4
+
5
+ From javascript:
6
+
7
+ import k from "@fraczak/k";
8
+
9
+ const fn = k.compile("<.name,.nom,'?'>");
10
+ console.log([{name:"x"},{nom:"y"},{}].map(fn));
11
+ // returns: [ "x", "y", "?" ]
12
+
13
+ ---
14
+
15
+ ## k - the way of building and manipulating JSON-like data
16
+
17
+ Technically, `k` is a notation for defining **first-order partial functions**.
18
+
19
+ An example of a _partial function_ is the _projection_, e.g., "`.toto`",
20
+ which maps an object to its property named `toto`, or it is not defined if
21
+ the property doesn't exist. E.g.,
22
+
23
+ 1 .toto :
24
+ 2 {"toto": 5, "titi": 10} --> 5
25
+ 3 {"titi": 10} ... undefined // it is not a value!
26
+
27
+ > Note: The above 3 lines should be read as follows. A `k`-expression
28
+ > is printed in the first line (before "`:`"). The following lines
29
+ > are examples of the function defined by the `k`-expression applayed
30
+ > to JSON values (first part of each line). If the function for the
31
+ > value is defined, then the result is printed after "`-->`" (line 2 in
32
+ > the above example). If the function is not defined for the value,
33
+ > then "`... undefined`" is printed (line 3).
34
+
35
+ ---
36
+
37
+ ### Combining "partial functions"
38
+
39
+ There are three ways of combining functions:
40
+
41
+ 1. **composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
42
+ nested field.
43
+
44
+ (.toto .titi) :
45
+ {"toto": {"titi": 10}} --> 10
46
+ {"toto": 10 } ... undefined
47
+ {} ... undefined
48
+
49
+ 2. **merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
50
+ `toto` if present; otherwise extracts `titi`.
51
+
52
+ <.toto, .titi> :
53
+ {"toto": 5, "titi": 10} --> 5
54
+ {"titi": 10 } --> 10
55
+ {} ... undefined
56
+
57
+ 3. **product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
58
+ .titi TITI}` extracts two fields and builds a record out of them.
59
+
60
+ {.toto TOTO, .titi TITI} :
61
+ {"toto": 5, "titi": 10, "x": 3} --> {"TOTO": 5, "TITI": 10}
62
+ {"titi": 10 } ... undefined
63
+
64
+ ---
65
+
66
+ **QUIZ**: What is:
67
+
68
+ - empty composition: `()` ?
69
+ - empty merge: `<>` ?
70
+ - empty product: `{}` ?
71
+ - `{{{{} s} s} s}` ?
72
+ - `({{{() a} b} c} .c .b .a)` ?
73
+
74
+ ---
75
+
76
+ ### Syntactic sugar
77
+
78
+ - parenthesis can be omitted, except for the empty composition `()`,
79
+ - dot (`.`) in "projection" acts as a separator so the space around it can be omitted.
80
+
81
+ For example, `[(.toto .titi (.0 .1))]` can be written as `[.toto.titi.0.1]`.
82
+
83
+ Comments can be introduced by `//`, `--`, `%`, or `#` and extends to
84
+ the end of line. Multiline `C`-like comments, `/* ... */`, are also
85
+ supported.
86
+
87
+ ### Basic extensions
88
+
89
+ #### Constants, i.e., literals for `strings`, `integers`, `booleans`, and `null`
90
+
91
+ A constant defines a function which ignores its argument and produces
92
+ the constant value. E.g.:
93
+
94
+ {123 int, "kScript" str, true bool, null null} :
95
+ "any" --> {"int":123,"str":"kScript","bool":true,"null":null}
96
+
97
+ Those values, i.e., `strings`, `integers`, `booleans`, and `null`, admit the
98
+ projection to the canonicat string representation of the value, e.g.:
99
+
100
+ .2 :
101
+ 2 --> {}
102
+ "2" --> {}
103
+ 4 ... undefined
104
+ true ... undefined
105
+
106
+ .true :
107
+ true --> {}
108
+ "true" --> {}
109
+ 4 ... undefined
110
+ "toto" ... undefined
111
+
112
+ .null :
113
+ null --> {}
114
+ "null" --> {}
115
+ 4 ... undefined
116
+ false ... undefined
117
+
118
+ #### Vector product
119
+
120
+ Vector product can be seen as an abbreviation for product whose field
121
+ names are integers starting from zero. E.g., `{.toto 0, .titi 1, 123 2}`
122
+ can be written as `[.toto, .titi, 123]`.
123
+
124
+ [.toto, .titi, 12] :
125
+ {"toto": 5, "titi": 10 } --> [5, 10, 12]
126
+
127
+ .1 :
128
+ ["A","B","C"] --> "B"
129
+ ["a"] ... undefined
130
+
131
+ ---
132
+
133
+ ### Pragmatic extensions, aka "standard library"
134
+
135
+ - `GT` -- identity for lists of decreasing elements; undefined otherwise
136
+
137
+ GT:
138
+ [4,3] --> [4,3]
139
+ [3,4] ... undefined
140
+ [] --> []
141
+ [4,3,0] --> [4,3,0]
142
+
143
+ - `EQ` -- identity for lists of equal elements; undefined otherwise
144
+
145
+ EQ:
146
+ [4,4] --> [4,4]
147
+ [4,5] ... undefined
148
+ [4,4,4] --> [4,4,4]
149
+ [] --> []
150
+
151
+ - `PLUS` and `TIMES` -- sum and product of lists of numbers
152
+
153
+ {PLUS plus, TIMES times} :
154
+ [1,2] --> {"plus":3,"times":2}
155
+ [2,2,2] --> {"plus":6,"times":8}
156
+ [] --> {"plus":0,"times":1}
157
+
158
+ - `CONCAT` -- concatenation of lists of strings
159
+
160
+ CONCAT:
161
+ ["a","bc","d"] --> "abcd"
162
+ ["a","bc","d"] --> "abcd"
163
+
164
+ - `toJSON` and `fromJSON` -- conversion to and from JSON strings
165
+
166
+ toJSON:
167
+ {"a": 12} --> "{\"a\":12}"
168
+
169
+ fromJSON:
170
+ "{\"a\":12}" --> {"a":12}
171
+ "2.12" --> 2.12
172
+ "[1,2.11,0.3e-32]" --> [1,2.11,3e-33]
173
+
174
+
175
+ - other predefined parial functions are: `DIV`, `FDIV`, `CONS`, `SNOC`, `toDateMsec`,
176
+ `toDateStr`, and `_log!`.
177
+
178
+ ---
179
+
180
+ ### Function and code (i.e., _type_) definitions
181
+
182
+ dec = [(),-1] PLUS;
183
+ max = <SNOC [.0, .1 max] <GT.0, .1>, .0> ;
184
+ factorial = < [(),0] GT .0 [dec factorial, ()] TIMES, 1 >;
185
+
186
+ <<<<<<< HEAD
187
+ _Codes_ (prefixed by `$`) can be defined by taged union and product. E.g.:
188
+ =======
189
+ ### Codes (schemas, i.e., _non-functional types_)
190
+
191
+ _Codes_ (prefixed by `$`) can be defined by tagged union and product. E.g.:
192
+ >>>>>>> b8f6bd5 (Minor doc changes)
193
+
194
+ $nat = <nat 1, {} 0>;
195
+ $pair = {nat x, nat y};
196
+
197
+ suc = {$nat 1};
198
+ add = $pair <{.x.1 x, .y suc y} add, .y>;
199
+
200
+ #### Basic extension codes
201
+
202
+ Since _basic extension_ introduces integers, booleans, and strings, there are three
203
+ predefined types: `int`, `bool`, and `string`. A vector product code can also be defined
204
+ by `[ codeExp ]`. All members of the vector are the same code. E.g.,
205
+
206
+ $intVector = [ int ];
207
+ $boolVector = [ bool ];
208
+ $tree = [ tree ];
209
+
210
+ emptyList? = $intVector $[ string ]; -- as only an empty vector can be a vector
211
+ -- of integers and a vector of strings
212
+ ---
213
+
214
+ ### List comprehension on vectors (experimental!)
215
+
216
+ A vector can be "open" by PIPE (`|`) operator so the following partial function is applied to
217
+ each element of the vector one by one, yielding another open value. An open value can be "closed",
218
+ i.e., turn into a regular vector using `CARET` (`^`) operator. E.g.:
219
+
220
+ ```k
221
+ | .x ^ :
222
+ [{x:12}, {x:8,y:10}, {y:98}] --> [12,8]
223
+ [1,2,3] --> []
224
+ ```
225
+
226
+ The PIPE operator can be used for defining the Cartesian product:
227
+
228
+ ```k
229
+ [.0 |, .1 |] ^ :
230
+ [[1,2], [3,4]] --> [[1,3],[1,4],[2,3],[2,4]]
231
+ ```
232
+
233
+ or
234
+
235
+ ```k
236
+ { | x, | y } ^ :
237
+ [1,2] --> [{"x":1,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":2}]
238
+ ```
239
+
240
+ **QUIZ**: Write a function which will take a list of integers and an integer `x`, and count how many
241
+ times value `x` appears in the list. (see `Examples/list-comprehension.k` for a solution)
242
+
243
+ ```k
244
+ count_occurrences =
245
+ $ { [int] list, int x }
246
+ -- ???
247
+ $ int;
248
+ ```
249
+
250
+ **WARNING**: When using `CARET` operator paranthesis may be required, e.g., like in:
251
+
252
+ ```k
253
+ | ( [|,|] ^ ) ^ :
254
+ [[1,2],[3,4]] --> [[[1,1],[1,2],[2,1],[2,2]],[[3,3],[3,4],[4,3],[4,4]]]
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Examples
260
+
261
+ 1. projection:
262
+
263
+ .x
264
+ ."field name"
265
+ .4
266
+
267
+ The function is defined only if its argument is a structure with
268
+ the field (or a vector with the index).
269
+
270
+ 2. _constants_, literals for Strings, Booleans, and
271
+ Integers. Examples:
272
+
273
+ "a string"
274
+ 'another "String"'
275
+ 123
276
+ false
277
+ null
278
+
279
+ 3. "built-in" functions:
280
+
281
+ [1, 2, 3] PLUS -- integer constant function 6
282
+ [4, 4] TIMES toJSON -- string constant function "16"
283
+ [3, 2] GT -- vector constant function [3, 2]
284
+ [3, 4] GT -- ... undefined
285
+
286
+ A more interesting example could be:
287
+
288
+ < GT .0, .1>
289
+
290
+ which selects the maximum element in two element vector, i.e.,
291
+
292
+ [3,8] < GT .0, .1 > --> 8
293
+
294
+ ### User defined functions
295
+
296
+ `k`-expression can be prefixed by function definitions. E.g.:
297
+
298
+ dec = [(),-1] PLUS;
299
+ zero? = [(),0] EQ 0;
300
+ factorial = <
301
+ zero? 1,
302
+ [dec factorial, ()] TIMES
303
+ >;
304
+ { () x, factorial "x!" }
305
+
306
+ Another example could be finding the biggest (max) value in a vector:
307
+
308
+ max = <
309
+ SNOC # [x0, x1, x2, ...] --> [x0, [x1, x2, ...]]
310
+ [.0, .1 max] # [x0, [x1, x2, ...]] --> [x0, max(x1,x2,...)], i.e., recursive call
311
+ <GT .0, .1> # if x0 > max(x1,x2,...) then x0 else max(x1,x2,...)
312
+ , # when SNOC is not defined, i.e., if the input vector has one element:
313
+ .0 # [x0] --> x0
314
+ >;
315
+ max
316
+
317
+ ### Value encodings (_codes_)
318
+
319
+ There are three predefined value encodings: `int`, `string`, and
320
+ `bool`. The language supports `code`-expressions:
321
+
322
+ - product, e.g., `{int x, int y, bool flag}`
323
+ - disjoint union, e.g., `<{} true, {} false>`
324
+ - vector, e.g., `[ int ]` (all elements of the vector use the same
325
+ encoding)
326
+
327
+ One can define recursive codes. E.g.:
328
+
329
+ $tree = <string leaf, {tree left, tree right} tree>;
330
+
331
+ Each _code_ definition starts with a `$`.
332
+ The above example defines new code called _tree_.
333
+
334
+ The code can be then used in a `k`-expression as a filter. A `code`-expression
335
+ within `k`-expression is again prefixed by `$`.
336
+
337
+ $ tree = <string leaf, {tree left, tree right} tree>;
338
+ inc = [(),1] PLUS;
339
+ max = <GT .0, .1>;
340
+ height = $ tree <
341
+ .leaf 0,
342
+ .tree [.left height, .right height] max inc
343
+ > $ int;
344
+ height
345
+
346
+ ---
347
+
348
+ ## `k` (a command-line JSON processor using `k` syntax)
349
+
350
+ There is a wrapper, `k` (`./node_modules/.bin/k`), which makes it easy to
351
+ run the language from command line.
352
+
353
+ > k
354
+ ... errors ...
355
+ Usage: ./node_modules/.bin/k ( k-expr | -k k-file) [ -1 ] [ json-file ]
356
+ E.g., cat '{"a": 10}' | ./node_modules/.bin/k '[(),()]'
357
+
358
+ For example:
359
+
360
+ 1. One `k`-expression with one `json`-object:
361
+
362
+ > echo '{"x": 12, "y": 13}' | k '{ <.x, "no x"> x, () input}'
363
+ {"x":12,"input":{"x":12,"y":13}}
364
+
365
+ 2. By providing only `k`-expression, the script will compile the
366
+ `k`-expression and apply the generated function to the `stdin`,
367
+ line by line:
368
+
369
+ > k '<["x=",.x," & y=",.y],["only x=",.x],["only y=",.y],["no x nor y"]>{CONCAT "x&y"}'
370
+
371
+ {"y": 123, "x": 432,"others": "..."} --> {"x&y":"x=432 & y=123"}
372
+ {"x": 987} --> {"x&y":"only x=987"}
373
+ {"z": 123} --> {"x&y":"no x nor y"}
374
+ ^D - to interrupt
375
+
376
+ If the input is a multiline json object, we need to add `-1` to the command-line options.
377
+
378
+ 3. If the `k`-expression is long, it can be put in a file, e.g.:
379
+
380
+ > cat test.k
381
+ --------- comments start by #, --, or // ----------------------------------
382
+ < -- merge of 4 partial functions...
383
+ ["x=", .x, " & y=", .y], -- produces a vector of 4 values, if fields 'x' and 'y' are present
384
+ ["only x=", .x], -- produces a pair '["only x=", "value-of-x"]', for input like {"x":"value-of-x"}
385
+ -- it is defined only if field 'x' is present
386
+ ["only y=", .y],
387
+ ["no x nor y"] -- defined for all input, returns always the same one element vector
388
+ >
389
+ -- one of the string vectors is passed to the following partial function,
390
+ -- which produces a record (map) with one field "x&y", whose value is the
391
+ -- result of concatenating elements of the passed in vector
392
+ { CONCAT "x&y" }
393
+ ------------------------------------------------------------------------------
394
+
395
+ We can use it by:
396
+
397
+ > k -k test.k
398
+
399
+ If we want to read `json` objects from a file, e.g., `my-objects.json`, we do
400
+
401
+ > k -k test.k my-objects.json
402
+ {"x&y":"x=432 & y=123"}
403
+ {"x&y":"only x=987"}
404
+ {"x&y":"no x nor y"}
405
+
406
+ where:
407
+
408
+ > cat my-objects.json
409
+ ####################################################
410
+ # empty lines and lines starting with # are ignored
411
+ {"y": 123, "x": 432,"others": "..."}
412
+ {"x": 987}
413
+ {"z": 123}
414
+ ####################################################
415
+
416
+ ---
417
+
418
+ ### Short comparaison with `jq` tutorial examples: <https://stedolan.github.io/jq/tutorial/>
419
+
420
+ 1.
421
+ curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | jq '.'
422
+ curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | k '()' -1
423
+ 2.
424
+ curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | jq '.[0]'
425
+ curl 'https://api.github.com/repositories/5101141/commits?per_page=5' | k '.0' -1
426
+ 3.
427
+ jq '.[0] | {message: .commit.message, name: .commit.committer.name}'
428
+ k '.0 {.commit.message message, .commit.committer.name name}' -1
429
+ 4. note: `k`-expression defines a partial function yielding a single json object, i.e., as far as `k` is concerned,
430
+ examples 4 and 5 of the `jq` tutorial are equivalent.
431
+ 5.
432
+ jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'
433
+ k '| .commit {.message message, .committer.name name} ^' -1
434
+ 6.
435
+ jq '[.[] | {message: .commit.message, name: .commit.committer.name, parents: [.parents[].html_url]}]'
436
+ k '| {.commit.message message, .commit.committer.name name, .parents | .html_url ^ parents} ^' -1
437
+
438
+ ---
439
+
440
+ ## k-REPL (Read-Evaluate-Print Loop)
441
+
442
+ Also there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`), which acts like a toy
443
+ shell for the language. E.g.:
444
+
445
+ > ./node_modules/.bin/k-repl
446
+ {'a' a, 'b' b} toJSON
447
+ => "{\"a\":\"a\",\"b\":\"b\"}"
448
+ {"a" a} toJSON fromJSON
449
+ => {"a": "a"}
450
+ inc = [(),1] PLUS; 1 inc inc
451
+ => 3
452
+ inc inc inc inc
453
+ => 7
454
+
455
+ ---
456
+
457
+ ## Using `k` from `javascript`
458
+
459
+ import k from "@fraczak/k";
460
+
461
+ let k_expression = '()';
462
+ k.run(k_expression,"ANYTHING...");
463
+ // RETURNS: "ANYTHING..."
464
+
465
+ k_expression = '{"ala" name, 23 age}';
466
+ k.run(k_expression,"ANYTHING...");
467
+ // RETURNS: {"name":"ala","age":23}
468
+
469
+ k_expression = '[.year, .age]';
470
+ k.run(k_expression,{"year":2002,"age":19});
471
+ // RETURNS: [2002,19]
472
+
473
+ k_expression = '[(), ()]';
474
+ k.run(k_expression,"duplicate me");
475
+ // RETURNS: ["duplicate me","duplicate me"]
476
+
477
+ k_expression = '[[[()]]]';
478
+ k.run(k_expression,"nesting");
479
+ // RETURNS: [[["nesting"]]]
480
+
481
+ k_expression = '[[()]] {() nested, .0.0 val}';
482
+ k.run(k_expression,"nesting and accessing");
483
+ // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
484
+
485
+ k_expression = '0000';
486
+ k.run(k_expression,{"test":"parse integer"});
487
+ // RETURNS: 0
488
+
489
+ k_expression = '[.y,.x] PLUS';
490
+ k.run(k_expression,{"x":3,"y":4});
491
+ // RETURNS: 7
492
+
493
+ var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
494
+
495
+ k_fn({"age":23,"name":"Emily"});
496
+ // RETURNS: {"nom":"Emily","age":23}
497
+
498
+ k_fn({"age":16,"name":"Katrina"});
499
+ // RETURNS: {"nom":"Katrina","age":"ado"}
500
+
501
+ k_fn({"age":2,"name":"Mark"});
502
+ // RETURNS: {"nom":"Mark","age":"enfant"}
503
+
504
+ k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
505
+
506
+ k_fn(1);
507
+ // RETURNS: 1
508
+
509
+ k_fn({"i":1});
510
+ // RETURNS: {"i":1}
511
+
512
+ k_fn([{"i":2},{"i":3},{"t":[]}]);
513
+ // RETURNS: undefined
514
+
515
+ k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
516
+ // RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
517
+
518
+ k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
519
+
520
+ k_fn({"None":"None"});
521
+ // RETURNS: {"None":"None"}
522
+
523
+ k_fn({"list":{"ints":[]}});
524
+ // RETURNS: {"list":{"ints":[]}}
525
+
526
+ k_fn({"list":{"ints":[1,2,3]}});
527
+ // RETURNS: {"list":{"ints":[1,2,3]}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "2.1.1",
3
+ "version": "2.1.4",
4
4
  "description": "k-language for JSON-like data transformation",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
package/parser.jison CHANGED
@@ -54,7 +54,7 @@ function fromEscString(escString) {
54
54
  /lex
55
55
 
56
56
  %token NAME STRING INT
57
- %token LA LC LB LP RA RP RB RC EQ DOT COMMA SC COL DOLLAR PIPE CARET
57
+ %token LA LC LB LP RA RP RB RC EQ DOT COMMA SC COL DOLLAR PIPE CARET
58
58
  %token EOF
59
59
 
60
60
  %start input_with_eof
@@ -113,6 +113,7 @@ code_label
113
113
  comp
114
114
  : exp { $$ = $1; }
115
115
  | comp exp { $$ = s.comp($1, $2); }
116
+ | comp CARET { $$ = {op: 'caret', caret: $1}; }
116
117
  ;
117
118
 
118
119
  exp
@@ -129,7 +130,6 @@ exp
129
130
  | DOT name { $$ = {op: "dot", dot: $2}; }
130
131
  | DOLLAR code { $$ = {op: "code", code: s.as_ref($2)}; }
131
132
  | PIPE { $$ = {op: "pipe"}; }
132
- | CARET { $$ = {op: "caret"}; }
133
133
  ;
134
134
 
135
135
  labelled