UNPKG

45.1 kBJavaScriptView Raw
1macro (->) {
2 rule infix { $a:ident | { $body ... } } => {
3 function($a) { $body ... }
4 }
5 rule infix { ($a:ident (,) ...) | { $body ... } } => {
6 function($a (,) ...) { $body ... }
7 }
8 rule infix { $a:ident | $body:expr } => {
9 function($a) { return $body }
10 }
11 rule infix { ($a:ident (,) ...) | $body:expr } => {
12 function($a (,) ...) { return $body }
13 }
14}
15
16macro compile {
17 case { $$mac $ctx $star:star $name ($args ...) { $body ... } } => {
18 var ctx = #{ $ctx };
19 var here = #{ here };
20 var fnName = #{ $name };
21 var matchStmt = false;
22 var matchArgs = #{ $args ... }.map(function(a) {
23 return a.expose().token.inner[0].expose().token.inner;
24 });
25
26 function input(stx, state) {
27 var pos = 0;
28 var inp = {
29 length: stx.length,
30 buffer: stx,
31 peek: peek,
32 take: take,
33 takeAPeek: takeAPeek,
34 back: back,
35 rest: rest,
36 state: state || {},
37 };
38 return inp;
39 function peek() {
40 if (arguments.length === 0) {
41 return [stx[pos]];
42 }
43 if (typeof arguments[0] === 'number') {
44 if (inp.length < arguments[0]) return;
45 return stx.slice(pos, pos + arguments[0]);
46 }
47 var res = [];
48 for (var i = 0, j = pos, t, a, m; i < arguments.length; i++) {
49 a = arguments[i];
50 t = stx[j++];
51 if (!matchesToken(a, t)) return;
52 res.push(t);
53 }
54 return res;
55 }
56 function take(len) {
57 if (len == null) len = 1;
58 var res = stx.slice(pos, pos + len);
59 pos += len;
60 inp.length -= len;
61 return res;
62 }
63 function takeAPeek() {
64 var res = peek.apply(null, arguments);
65 if (res) return take(res.length);
66 }
67 function back(len) {
68 if (len == null) len = 1;
69 pos -= len;
70 inp.length += len;
71 }
72 function rest() {
73 return stx.slice(pos);
74 }
75 }
76 function environment(vars) {
77 var env = _.extend({
78 set: set,
79 stash: stash,
80 retrieve: retrieve
81 }, vars);
82 return env;
83 function set(mod) {
84 return environment(extend({}, vars, mod));
85 }
86 function stash(k, v) {
87 assert(v, 'Expected value for ' + k);
88 var spec = {};
89 spec[k] = v;
90 return set({
91 refs: extend({}, vars.refs, spec)
92 });
93 }
94 function retrieve(k) {
95 assert(vars.refs.hasOwnProperty(k), 'Ref does not exist for ' + k);
96 return vars.refs[k];
97 }
98 }
99 function equals(x, y) {
100 if (x && x.equals) {
101 return x.equals(y);
102 }
103 if (Array.isArray(x)) {
104 return arrayEquals(x, y);
105 }
106 return x === y;
107 }
108 function arrayEquals(x, y) {
109 if (!Array.isArray(y) || x.length !== y.length) {
110 return false;
111 }
112 for (var i = 0; i < x.length; i++) {
113 if (!equals(x[i], y[i])) {
114 return false;
115 }
116 }
117 return true;
118 }
119 function extend(o) {
120 for (var i = 1; i < arguments.length; i++) {
121 var a = arguments[i];
122 var k = Object.keys(a);
123 for (var j = 0; j < k.length; j++) {
124 o[k[j]] = a[k[j]];
125 }
126 }
127 return o;
128 }
129 function constant(x) {
130 return () -> x;
131 }
132 function assert(cond, msg) {
133 if (!cond) throw new Error('Assertion failure: ' + msg);
134 }
135 function concat(x, y) {
136 return x.concat(y);
137 }
138 function repeat(len, f) {
139 if (!len) return [];
140 var res = Array(len);
141 for (var i = 0; i < len; i++) res[i] = f(i);
142 return res;
143 }
144 function join(j, arr) {
145 if (!arr.length) return [];
146 return arr.reduce((a, b) -> a.concat(j, b));
147 }
148 function matchesToken(tmpl, t) {
149 if (t && t.length === 1) t = t[0];
150 if (!t || tmpl.type && t.token.type !== tmpl.type
151 || tmpl.value && t.token.value !== tmpl.value) return false;
152 return true;
153 }
154 function prependReturn(stx) {
155 if (matchesToken({ type: T.Keyword, value: 'return' }, stx[0])) {
156 return stx;
157 }
158 var ret = makeKeyword('return', stx[0])
159 return [ret].concat(stx);
160 }
161 function forceReturn(stx) {
162 var needsReturn = true;
163 var inp = input(stx);
164 var res = [], toks;
165 while (inp.length) {
166 if (toks = inp.takeAPeek({ type: T.Keyword }, PARENS, RETURN)) {
167 res = res.concat(toks);
168 } else if (toks = inp.takeAPeek(RETURN)) {
169 needsReturn = false;
170 res.push(toks[0]);
171 } else {
172 res.push(inp.take()[0]);
173 }
174 }
175 if (needsReturn) res.push(makeKeyword('return', here));
176 return res;
177 }
178 function syntaxError(tok, err, info) {
179 if (!err) err = 'Unexpected token';
180 if (info) err += ' (' + info + ')';
181 throwSyntaxError('sparkler', err, tok);
182 }
183 var refId = 0;
184 function makeRef(ctx) {
185 if (!ctx) ctx = here;
186 return [makeIdent('r' + (refId++), ctx)];
187 }
188 function makeAssign(kw, ident, rhs, ctx) {
189 if (!ctx) ctx = here;
190 return (kw || [makeKeyword('var', ctx)]).concat(
191 ident,
192 rhs ? [makePunc('=', ctx)].concat(rhs) : [],
193 makePunc(';', ctx)
194 );
195 }
196 function replaceIdents(guard, names) {
197 function traverse(arr) {
198 var stx = [];
199 for (var i = 0, s; s = arr[i]; i++) {
200 if (s.token.type === T.Delimiter) {
201 var clone = cloneSyntax(s.expose());
202 s.token.inner = traverse(s.token.inner);
203 stx.push(s);
204 } else if (s.token.type === T.Identifier &&
205 names.hasOwnProperty(s.token.value)) {
206 stx.push.apply(stx, names[s.token.value]);
207 } else {
208 stx.push(s);
209 }
210 }
211 return stx;
212 }
213 return traverse(guard);
214 }
215 function cloneSyntax(stx) {
216 function F(){}
217 F.prototype = stx.__proto__;
218 var s = new F();
219 extend(s, stx);
220 s.token = extend({}, s.token);
221 return s;
222 }
223 function Data(t, args) {
224 this.tag = t;
225 this.args = args;
226 }
227 Data.prototype = {
228 unapply : f -> f ? f.apply(null, this.args) : this.args.slice(),
229 equals : that -> equalsTag.call(this, that) && arrayEquals(this.args, that.args)
230 };
231 function data(t, fs, proto) {
232 var D = () -> {};
233 D.prototype = Data.prototype;
234 var Ctr = args -> {
235 Data.call(this, t, args);
236 };
237 Ctr.prototype = new D();
238 Ctr.prototype.constructor = Ctr;
239 Ctr.prototype['is' + t] = true;
240 extend(Ctr.prototype, proto || {});
241 fs.forEach((f, i) -> {
242 Object.defineProperty(Ctr.prototype, f, {
243 writeable: false,
244 configurable: false,
245 get: () -> this.args[i]
246 });
247 });
248 var arity = fs.length;
249 return arity === 0 ? () -> new Ctr([])
250 : arity === 1 ? (x) -> new Ctr([x])
251 : arity === 2 ? (x, y) -> new Ctr([x, y])
252 : () -> {
253 var args = Array(arguments.length);
254 for (var i = 0; i < arguments.length; i++) {
255 args[i] = arguments[i];
256 }
257 return new Ctr(args);
258 };
259 }
260 var Fun = data('Fun', ['length']);
261 var Match = data('Match', ['length']);
262 var Args = data('Args', []);
263 var Arr = data('Arr', []);
264 var Unapply = data('Unapply', []);
265 var UnapplyObj = data('UnapplyObj', []);
266 var Obj = data('Obj', []);
267 var Wild = data('Wild', []);
268 var Undef = data('Undef', []);
269 var Unit = data('Unit', []);
270 var Inst = data('Inst', []);
271 var Lit = data('Lit', ['lit']);
272 var Extractor = data('Extractor', ['name']);
273 var Arg = data('Arg', ['index']);
274 var Len = data('Len', ['length']);
275 var LenMin = data('LenMin', ['length']);
276 var Index = data('Index', ['index']);
277 var IndexNoop = data('IndexNoop', ['index']);
278 var KeyVal = data('KeyVal', ['key']);
279 var KeyIn = data('KeyIn', ['key']);
280 var KeyNoop = data('KeyNoop', ['key']);
281 var Rest = data('Rest', ['pattern', 'names']);
282 var RestEnd = data('RestEnd', []);
283 var Case = data('Case', []);
284 var Guard = data('Guard', [], { equals: constant(false) });
285 var Body = data('Body', [], { equals: constant(false) });
286 var Backtrack = data('Backtrack', []);
287 var NoMatch = data('NoMatch', []);
288 var Branch = data('Branch', ['node', 'branches']);
289 var Leaf = data('Leaf', ['node']);
290 var Ann = data('Ann', ['value', 'ann'], { equals: equalsFst });
291 var Group = data('Group', ['node', 'matrix', 'stack']);
292 function equalsTag(that) {
293 return that && that.tag === this.tag;
294 }
295 function equalsFst(that) {
296 return equalsTag.call(this, that) && equals(this.args[0], that.args[0]);
297 }
298 function optimizeSyntax(stx) {
299 var inp = input(stx);
300 var res = [];
301 var toks, opt;
302 while (inp.length) {
303 if (inp.peek(USERCODE)) {
304 res.push.apply(res, inp.take(2)[1].token.inner);
305 break;
306 } else if (toks = inp.takeAPeek({ type: T.Keyword }, PARENS, BRACES)) {
307 if (matchesToken(IF, toks[0])) {
308 opt = optimizeIfs(toks);
309 } else if (matchesToken(FOR, toks[0])) {
310 opt = optimizeFors(toks);
311 } else {
312 toks[2].token.inner = optimizeSyntax(toks[2].token.inner);
313 opt = toks;
314 }
315 res = res.concat(opt);
316 } else if (toks = inp.takeAPeek(BRACES)) {
317 res = res.concat(optimizeSyntax(toks[0].token.inner));
318 break;
319 } else if (toks = inp.takeAPeek(CONTINUE)) {
320 res.push(toks[0]);
321 break;
322 } else {
323 res.push(inp.take()[0]);
324 }
325 }
326 return res;
327 }
328 function optimizeIfs(stx) {
329 var pred = stx[1];
330 var block = stx[2];
331 var inner = input(optimizeSyntax(block.token.inner));
332 var toks = inner.takeAPeek(IF, PARENS, BRACES);
333 if (toks && inner.length === 0) {
334 pred.token.inner = [makeDelim('()', pred.token.inner, pred), makePunc('&&', here), toks[1]];
335 stx[2] = toks[2];
336 } else if (toks) {
337 block.token.inner = toks.concat(inner.rest());
338 } else {
339 block.token.inner = inner.rest();
340 }
341 return stx;
342 }
343 function optimizeFors(stx) {
344 var inner = optimizeSyntax(stx[2].token.inner);
345 for (var i = 0, t; t = inner[i]; i++) {
346 if (matchesToken({ type: T.Keyword, value: 'continue' }, t)) {
347 inner = inner.slice(0, i);
348 break;
349 }
350 }
351 stx[2].token.inner = inner;
352 return stx;
353 }
354 var T = parser.Token;
355 var EQ = { type: T.Punctuator, value: '=' };
356 var ARROW = { type: T.Punctuator, value: '=>' };
357 var REST = { type: T.Punctuator, value: '...' };
358 var COLON = { type: T.Punctuator, value: ':' };
359 var AT = { type: T.Punctuator, value: '@' };
360 var COMMA = { type: T.Punctuator, value: ',' };
361 var PERIOD = { type: T.Punctuator, value: '.' };
362 var WILDCARD = { type: T.Punctuator, value: '*' };
363 var SCOLON = { type: T.Punctuator, value: ';' };
364 var UNDEF = { type: T.Identifier, value: 'undefined' };
365 var VOID = { type: T.Keyword, value: 'void' };
366 var CASE = { type: T.Keyword, value: 'case' };
367 var VAR = { type: T.Keyword, value: 'var' };
368 var IF = { type: T.Keyword, value: 'if' };
369 var ELSE = { type: T.Keyword, value: 'else' };
370 var FOR = { type: T.Keyword, value: 'for' };
371 var RETURN = { type: T.Keyword, value: 'return' };
372 var CONTINUE = { type: T.Keyword, value: 'continue' };
373 var VAR = { type: T.Keyword, value: 'var' };
374 var LET = { type: T.Keyword, value: 'let' };
375 var CONST = { type: T.Keyword, value: 'const' };
376 var DEFAULT = { type: T.Keyword, value: 'default' };
377 var BRACKETS = { type: T.Delimiter, value: '[]' };
378 var PARENS = { type: T.Delimiter, value: '()' };
379 var BRACES = { type: T.Delimiter, value: '{}' };
380 var IDENT = { type: T.Identifier };
381 var BOOL = { type: T.BooleanLiteral };
382 var NULL = { type: T.NullLiteral };
383 var STRING = { type: T.StringLiteral };
384 var NUMBER = { type: T.NumericLiteral };
385 var PUNC = { type: T.Punctuator };
386 var USERCODE = { type: T.Identifier, value: '$sparkler__user__' + __fresh() };
387 function parse(stx) {
388 var inp = input(stx);
389 var cases = [];
390 var patts = [];
391 var i = 0;
392 if (inp.peek(CASE) || inp.peek(DEFAULT)) {
393 matchStmt = true;
394 }
395 while (inp.length) {
396 var list = scanArgumentList(inp);
397 var first = list[0];
398 var guard = scanGuard(inp);
399 var body = scanCaseBody(inp);
400 var inp2 = input(list, { idents: [] });
401 var args = parseArgumentList(inp2);
402 if (!guard.length) {
403 if (patts.some(p -> p.equals(args))) {
404 syntaxError(first, 'Duplicate case');
405 } else {
406 patts.push(args);
407 }
408 }
409 cases.push(args.unapply((v, bs) -> {
410 var b = Leaf(Ann(Body(), { stx: body, stashed: inp2.state.idents }));
411 var g = guard.length
412 ? Branch(Ann(Guard(), { stx: guard, stashed: inp2.state.idents }), [b])
413 : b;
414 return Branch(Ann(Case(), { index: i++ }), [Branch(v, bs), g]);
415 }));
416 }
417 var len = Math.max.apply(null, cases.map(c -> c.branches[0].node.ann.length));
418 var exh = cases.reduce((acc, c, i) -> {
419 c.branches[0].node.ann.length = len;
420 return !acc && !c.branches[1].node.value.isGuard
421 && c.branches[0].branches.every(a -> a.branches[0].node.value.isWild);
422 }, false);
423 if (exh) {
424 cases[cases.length - 1].branches[1].node.ann.last = true;
425 } else {
426 cases.push(Branch(Ann(Case(), {}),
427 [Branch(Ann(Args(), { length: len }),
428 [Branch(Ann(Arg(0), {}),
429 [Leaf(Ann(Wild(), {}))])]),
430 Leaf(Ann(NoMatch(), {}))]));
431 }
432 return matchStmt
433 ? Branch(Ann(Match(len), { exhaustive: exh }), cases)
434 : Branch(Ann(Fun(len), { exhaustive: exh }), cases);
435 }
436 function scanArgumentList(inp) {
437 var res;
438 if (matchStmt) {
439 res = inp.takeAPeek(CASE);
440 if (!res) {
441 res = inp.takeAPeek(DEFAULT);
442 if (res) return res;
443 syntaxError(res, null, 'maybe you meant case');
444 }
445 }
446 res = inp.takeAPeek(PARENS);
447 if (res) {
448 if (inp.peek(IF) || (matchStmt ? inp.peek(COLON) : inp.peek(ARROW))) return res[0].expose().token.inner;
449 if (inp.peek(EQ)) syntaxError(inp.take(), null, 'maybe you meant =>');
450 throw syntaxError(inp.take());
451 }
452 res = [];
453 while (inp.length) {
454 if (inp.peek(IF) || (matchStmt ? inp.peek(COLON) : inp.peek(ARROW))) return res;
455 if (inp.peek(EQ)) syntaxError(inp.take(), null, 'maybe you meant =>');
456 if (inp.peek(COMMA)) syntaxError(inp.take(), null, 'multiple parameters require parens');
457 res.push(inp.take()[0]);
458 }
459 if (res.length) syntaxError(res[res.length - 1], 'Case body required');
460 else syntaxError(tok, 'Argument list required');
461 }
462 function scanGuard(inp) {
463 var tok = inp.takeAPeek(IF);
464 if (!tok) return [];
465 var res = [];
466 while (inp.length) {
467 if (matchStmt ? inp.peek(COLON) : inp.peek(ARROW)) {
468 if (!res.length) syntaxError(tok, 'Guard required');
469 return res;
470 }
471 res.push(inp.take()[0]);
472 }
473 if (res.length) syntaxError(res[res.length - 1], 'Case body required');
474 else syntaxError(tok, 'Guard required');
475 }
476 function scanCaseBody(inp) {
477 inp.take(1);
478 var res = inp.takeAPeek(BRACES);
479 if (res) {
480 if (matchStmt) {
481 return res[0].expose().token.inner;
482 } else {
483 inp.takeAPeek(COMMA);
484 return forceReturn(res[0].expose().token.inner);
485 }
486 }
487 res = [];
488 while (inp.length) {
489 if (matchStmt ? inp.peek(CASE) || inp.peek(DEFAULT) : inp.takeAPeek(COMMA)) break;
490 res.push(inp.take(1)[0]);
491 }
492 return matchStmt ? res : prependReturn(res);
493 }
494 function parseArgumentList(inp) {
495 if (!inp.length) {
496 return Branch(Ann(Args(), { length: 0 }),
497 [Branch(Ann(Arg(0), {}),
498 [Leaf(Ann(Unit(), {}))])]);
499 }
500 var res = inp.takeAPeek(DEFAULT);
501 if (res) {
502 if (inp.length) syntaxError(inp.take());
503 return Branch(Ann(Args(), { length: 0 }),
504 [Branch(Ann(Arg(0), {}),
505 [Leaf(Ann(Wild(), {}))])]);
506 }
507 var len = 0;
508 var args = parseRestPatterns(inp).map((p, i, ps) -> {
509 if (p.node.value.isRest) {
510 if (i === ps.length - 1) {
511 p.node.ann.argRest = true;
512 p.node.ann.start = i;
513 } else {
514 syntaxError(p.args[1].stx, 'Rest arguments are only allowed at the end');
515 }
516 } else {
517 len++;
518 }
519 return Branch(Ann(Arg(i), {}), [p]);
520 });
521 return Branch(Ann(Args(), { length: len }), args);
522 }
523 function parseRestPatterns(inp) {
524 return commaSeparated(parseRestPattern, inp, multiRestCallback());
525 }
526 function parseRestPattern(inp) {
527 return parseRest(inp) || parsePattern(inp);
528 }
529 function parsePattern(inp) {
530 return parseWildcard(inp)
531 || parseUndefined(inp)
532 || parseLiteral(inp)
533 || parseArray(inp)
534 || parseObject(inp)
535 || parseExtractor(inp)
536 || parseBinder(inp)
537 || parseIdentifier(inp);
538 }
539 function parseRest(inp) {
540 var res = inp.takeAPeek(REST);
541 if (res) {
542 var len = inp.state.idents.length;
543 var patt = parsePattern(inp);
544 var idents = inp.state.idents.slice(len);
545 var names = idents.map(id -> unwrapSyntax(id.ident));
546 return Leaf(Ann(Rest(patt || Leaf(Ann(Wild(), {})), names),
547 { stx: res, stashed: inp.state.idents.slice(len) }));
548 }
549 }
550 function parseWildcard(inp) {
551 var res = inp.takeAPeek(WILDCARD);
552 if (res) return Leaf(Ann(Wild(), { stx: res }));
553 }
554 function parseUndefined(inp) {
555 var res = inp.takeAPeek(VOID);
556 if (res) {
557 if (!inp.peek(PUNC)) {
558 return Leaf(Ann(Undef(), { stx: res.concat(inp.take(1)) }));
559 }
560 }
561 res = inp.takeAPeek(UNDEF);
562 if (res) return Leaf(Ann(Undef(), { stx: res }));
563 }
564 function parseLiteral(inp) {
565 var stx = inp.peek(1);
566 if (matchesToken(NULL, stx) || matchesToken(NUMBER, stx) ||
567 matchesToken(STRING, stx) || matchesToken(BOOL, stx)) {
568 var res = inp.take(1);
569 return Leaf(Ann(Lit(unwrapSyntax(res)), { stx: res }));
570 }
571 }
572 function parseExtractor(inp) {
573 var stx = [], tok;
574 while (tok = inp.peek()) {
575 if (stx.length === 0 && matchesToken(IDENT, tok) ||
576 stx.length && matchesToken(IDENT, stx[0]) && matchesToken(PERIOD, tok) ||
577 stx.length && matchesToken(IDENT, tok) && matchesToken(PERIOD, stx[0])) {
578 stx.unshift(inp.take()[0]);
579 } else break;
580 }
581 if (stx.length) {
582 if (matchesToken(PERIOD, stx[0])) syntaxError(stx[0]);
583 var name = stx[0].token.value;
584 if (name[0].toUpperCase() === name[0] &&
585 name[0] !== '$' && name[0] !== '_') {
586 var ext = parseUnapply(inp) || parseUnapplyObj(inp) || Leaf(Ann(Inst(), {}));
587 var nameStr = stx.reverse().map(unwrapSyntax).join('');
588 extend(ext.node.ann, { extractor: stx, name: nameStr });
589 return Branch(Ann(Extractor(nameStr), { stx: stx }), [ext]);
590 } else {
591 inp.back(stx.length);
592 }
593 }
594 }
595 function parseArrayLike(delim, ctr, inp) {
596 var stx = inp.takeAPeek(delim);
597 if (stx) {
598 var inp2 = input(stx[0].expose().token.inner, inp.state);
599 var inner = parseRestPatterns(inp2)
600 var len = arrayLen(inner);
601 var withIndex = inner.reduce((acc, p, i, arr) -> {
602 var ann = {}, pann, stop, node;
603 if (p.node.value.isRest) {
604 if (i === 0) {
605 stop = -(arr.length - 1);
606 } else if (i === arr.length - 1) {
607 stop = 0;
608 } else {
609 stop = -(arr.length - i - 1);
610 }
611 extend(p.node.ann, { start: i, stop: stop });
612 node = Ann(IndexNoop(acc[0] + 1), ann);
613 } else {
614 stop = acc[0] + 1;
615 node = Ann(Index(acc[0]), ann);
616 }
617 return [stop, acc[1].concat(Branch(node, [p]))];
618 }, [0, []]);
619 return Branch(Ann(ctr(), {}),
620 [inner.length ? Branch(len, withIndex[1]) : Leaf(len)]);
621 }
622 }
623 function parseArray(inp) {
624 return parseArrayLike(BRACKETS, Arr, inp);
625 }
626 function parseUnapply(inp) {
627 return parseArrayLike(PARENS, Unapply, inp);
628 }
629 function parseObjectLike(ctr, inp) {
630 var stx = inp.takeAPeek(BRACES);
631 if (stx) {
632 var inp2 = input(stx[0].expose().token.inner, inp.state);
633 var inner = commaSeparated(parseObjectPattern, inp2);
634 return Branch(Ann(ctr(), {}), inner);
635 }
636 }
637 function parseUnapplyObj(inp) {
638 var res = parseObjectLike(UnapplyObj, inp);
639 if (res) {
640 res.branches.forEach(b -> b.node.ann.hasOwn = true);
641 return res;
642 }
643 }
644 function parseObject(inp) {
645 return parseObjectLike(Obj, inp);
646 }
647 function parseObjectPattern(inp) {
648 var res = parseBinder(inp);
649 if (res) {
650 var ann = res.node.ann;
651 var name = unwrapSyntax(ann.idents[0]);
652 return Branch(Ann(KeyIn(name), ann),
653 [Branch(Ann(KeyVal(name), ann), [res])]);
654 }
655 var tok = inp.takeAPeek(IDENT) || inp.takeAPeek(STRING);
656 if (tok) {
657 var name = unwrapSyntax(tok);
658 var ann = { stx: tok };
659 if (inp.takeAPeek(COLON)) {
660 var patt = parsePattern(inp);
661 if (patt) {
662 return Branch(Ann(KeyIn(name), ann),
663 [Branch(Ann(KeyVal(name), ann), [patt])]);
664 }
665 syntaxError(inp.take(), null, 'not a pattern');
666 }
667 if (matchesToken(IDENT, tok)) {
668 inp.state.idents.push({ ident: tok[0] });
669 return Branch(Ann(KeyIn(name), ann),
670 [Branch(Ann(KeyVal(name), ann),
671 [Leaf(Ann(Wild(), { idents: [tok[0]] }))])]);
672 }
673 return Leaf(Ann(KeyIn(name), ann));
674 }
675 }
676 function parseBinder(inp) {
677 var kw = inp.takeAPeek(VAR) || inp.takeAPeek(LET) || inp.takeAPeek(CONST);
678 var res = inp.takeAPeek(IDENT, AT);
679 if (res) {
680 var patt = parsePattern(inp);
681 if (patt) {
682 inp.state.idents.push({
683 ident: [res[0]],
684 keyword: kw,
685 });
686 patt.node.ann.idents = [res[0]];
687 return patt;
688 }
689 syntaxError(inp.take(), null, 'not a pattern');
690 }
691 if (kw) inp.back();
692 }
693 function parseIdentifier(inp) {
694 var kw = inp.takeAPeek(VAR) || inp.takeAPeek(LET) || inp.takeAPeek(CONST);
695 var res = inp.takeAPeek(IDENT);
696 if (res) {
697 inp.state.idents.push({
698 ident: res,
699 keyword: kw
700 });
701 return Leaf(Ann(Wild(), { idents: res }));
702 }
703 if (kw) inp.back();
704 }
705 function commaSeparated(parser, inp, cb) {
706 var all = [], res;
707 while (inp.length) {
708 res = parser(inp);
709 if (res && !cb || res && cb(res, inp)) {
710 all.push(res);
711 if (!inp.takeAPeek(COMMA) && inp.length) {
712 syntaxError(inp.take(), null, 'maybe you meant ,');
713 }
714 } else if (!res) {
715 syntaxError(inp.take());
716 }
717 }
718 return all;
719 }
720 function multiRestCallback() {
721 var count = 0;
722 return function(res, inp) {
723 return res.unapply((v, ann) -> {
724 if (v.tag === 'Rest' && count++) {
725 syntaxError(ann.stx, 'Multiple ...s are not allowed');
726 }
727 return true;
728 });
729 }
730 }
731 function arrayLen(bs) {
732 var ctr = bs.reduce((ctr, b) -> {
733 return b.node.value.isRest
734 ? [LenMin, ctr[1]]
735 : [ctr[0], ctr[1] + 1]
736 }, [Len, 0]);
737 return Ann(ctr[0](ctr[1]), {});
738 }
739 function compile(ast) {
740 return compilePattern(astToTree(ast), environment({ refs: {} }), []);
741 }
742 function astToTree(ast) {
743 var cases = ast.branches.map(b -> [annotateLevels(b.branches[0], 0)]);
744 var frame = ast.branches.map(b -> [b.branches[1]]);
745 var gr = groupRows(cases, [frame]);
746 return Branch(ast.node, [transformCase(gr[0].node, gr[0].matrix, gr[0].stack)]);
747 }
748 function annotateLevels(ast, l) {
749 ast.node.ann.level = l;
750 if (ast.branches) {
751 ast.branches.forEach(b -> annotateLevels(b, l + 1));
752 }
753 return ast;
754 }
755 function transformCase(c, m, stack) {
756 if (!stack.length) {
757 return m.length
758 ? Branch(c, m.reduce(concat))
759 : Leaf(c);
760 }
761 var gr;
762 if (!m.length) {
763 gr = groupRows(stack[0], stack.slice(1));
764 } else if (isMatrixType(c.value)) {
765 gr = groupRows(scoreMatrix(normalizeMatrix(c, m)), stack);
766 } else {
767 gr = groupRows(m, stack);
768 }
769 var head = transformCase(gr[0].node, gr[0].matrix, gr[0].stack);
770 var rest = gr[1].length && transformCase(c, gr[1], gr[2]).branches || [];
771 var cons = [head].concat(rest);
772 return Branch(c, matchStmt
773 ? mergeBranchesWithBacktrack(cons)
774 : mergeBranches(cons));
775 }
776 function groupRows(m, stack) {
777 function rowHeadStack(r, s) {
778 return r.length === 1 ? s : [[r.slice(1)]].concat(s);
779 }
780 var head = m[0];
781 var rest = m.slice(1);
782 var init = Group(head[0].node,
783 head[0].branches ? [head[0].branches] : [],
784 rowHeadStack(head, stackRow(stack, 0)));
785 return rest.reduce((acc, r, i) -> {
786 var g = acc[0];
787 var c = r[0];
788 if (!acc[1].length && canGroupCases(head[0], c)) {
789 acc[0] = Group(mergeNodes(g.node, c.node),
790 c.branches ? g.matrix.concat([c.branches]) : g.matrix,
791 stackZip(g.stack, rowHeadStack(r, stackRow(stack, i + 1))));
792 } else {
793 acc[1].push(r);
794 acc[2] = stackZip(acc[2], stackRow(stack, i + 1));
795 }
796 return acc;
797 }, [init, [], []]);
798 }
799 function mergeBranches(bs) {
800 return bs.reduceRight((acc, c) -> {
801 if (acc.length && acc[0].node.equals(c.node)) {
802 acc[0] = Branch(mergeNodes(c.node, acc[0].node),
803 c.branches.concat(acc[0].branches));
804 } else {
805 acc.unshift(c);
806 }
807 return acc;
808 }, []);
809 }
810 function mergeBranchesWithBacktrack(bs) {
811 return bs.reduceRight((acc, c, i) -> {
812 var head = acc[0];
813 if (head && c.node.equals(head.node)) {
814 acc[0] = Branch(mergeNodes(c.node, head.node),
815 c.branches.concat(Branch(Ann(Backtrack(), {}), head.branches)));
816 } else if (head) {
817 acc[0] = Branch(c.node,
818 c.branches.concat(Branch(Ann(Backtrack(), {}),
819 [Branch(head.node, head.branches)])));
820 } else {
821 acc.unshift(c);
822 }
823 return acc;
824 }, []);
825 }
826 function mergeNodes(c1, c2) {
827 return Ann(c1.value,
828 extend({}, c1.ann, {
829 idents: (c1.ann.idents || []).concat(c2.ann.idents || [])
830 }));
831 }
832 function canGroupCases(c1, c2) {
833 if (canGroup.hasOwnProperty(c1.node.value.tag)) {
834 return canGroup[c1.node.value.tag](c1, c2);
835 } else {
836 return c1.node.equals(c2.node);
837 }
838 }
839 var canGroup = {
840 Arg: canGroupChild,
841 Index: canGroupChild,
842 };
843 function canGroupChild(c1, c2) {
844 return c1.node.equals(c2.node)
845 && canGroupCases(c1.branches[0], c2.branches[0]);
846 }
847 function normalizeMatrix(c, m) {
848 assert(isMatrixType(c.value), 'Unsupported matrix type: ' + c.value.tag);
849 return normalize[c.value.tag](c, m);
850 }
851 function isMatrixType(n) {
852 return normalize.hasOwnProperty(n.tag);
853 }
854 var normalize = {
855 Obj : normalizeObj,
856 UnapplyObj : normalizeObj,
857 Len : normalizeNoop,
858 LenMin : normalizeNoop,
859 Args: (n, m) -> {
860 var max = Math.max.apply(null, m.map(r -> r.length));
861 return normalizeVarLen(max, Arg, m)
862 }
863 };
864 function normalizeNoop(n, m) {
865 return m;
866 }
867 function normalizeObj(n, m) {
868 var layout = m.reduceRight((acc, r) -> {
869 return r.reduce((a, c) -> {
870 a[0][0].keys[c.node.value.key] = true;
871 a[1][c.node.value.key] = true;
872 return a;
873 }, [[{ keys: {}, row: r }].concat(acc[0]), acc[1]]);
874 }, [[], {}]);
875 return layout[0].map(r -> {
876 Object.keys(layout[1]).forEach(k -> {
877 if (!r.keys[k]) r.row.push(Leaf(Ann(KeyNoop(k), {})));
878 });
879 return r.row.sort(sortObjKeys);
880 });
881 }
882 function normalizeVarLen(len, ctr, m) {
883 return m.map(r -> {
884 if (r.length >= len) {
885 return r;
886 } else {
887 return r.concat(repeat(len - r.length, i -> {
888 return Leaf(Ann(ctr(r.length - i + 1), {}));
889 }));
890 }
891 });
892 }
893 function sortObjKeys(a, b) {
894 a = a.node.value.key;
895 b = b.node.value.key;
896 return a < b ? -1 : b < a ? 1 : 0;
897 }
898 function scoreMatrix(m) {
899 var scores = m.map(c -> c.map(scorePattern));
900 var ranks = [];
901 for (var i = 0; i < scores[0].length; i++) {
902 var s = 0;
903 for (var j = 0; j < scores.length; j++) {
904 if (scores[j][i] > 0) {
905 s += scores[j][i];
906 } else {
907 break;
908 }
909 }
910 for (var k = 0; k <= ranks.length; k++) {
911 if (!ranks[k] || s > ranks[k][0]) {
912 ranks.splice(k, 0, [s, i]);
913 break;
914 }
915 }
916 }
917 return m.map(c -> ranks.map(r -> c[r[1]]));
918 }
919 function scorePattern(p) {
920 var t = p.node.value.tag;
921 return t in score ? score[t].apply(score, p.args) : 1;
922 }
923 function scoreChild(n, bs) {
924 return bs ? scorePattern(bs[0]) : 0;
925 }
926 var score = {
927 Arg : scoreChild,
928 Index : scoreChild,
929 Wild : constant(0),
930 KeyNoop : constant(0),
931 IndexNoop : scoreChild,
932 };
933 function stackRow(stack, i) {
934 return stack.map(m -> [m[i]]);
935 }
936 function stackZip(s1, s2) {
937 if (!s1.length) return s2;
938 if (!s2.length) return s1;
939 return s1.map((m, i) -> m.concat(s2[i]));
940 }
941 function compilePattern(t, env, stack) {
942 var n = t.node;
943 var bs = t.branches;
944 if (t.isBranch) {
945 var c = branchCompilers[n.value.tag] || assert(false, 'Unexpected node: ' + n.value.tag);
946 var r = stack[stack.length - 1];
947 var cont = (e, r2) -> {
948 var s = stack.concat([r2 || r]);
949 return bs.reduce((stx, b, i) -> {
950 var l = b.node.ann.level;
951 return stx.concat(compilePattern(b, e, s.slice(0, l)));
952 }, []);
953 };
954 if (n.ann.idents && n.ann.idents.length) {
955 env = n.ann.idents.reduce((e, id) -> {
956 return e.stash(unwrapSyntax(id), r);
957 }, env);
958 }
959 return c.apply(null, n.value.unapply().concat(n.ann, [r], env, cont, [bs]));
960 } else {
961 var c = leafCompilers[n.value.tag] || assert(false, 'Unexpected leaf: ' + n.value.tag);
962 return c.apply(null, n.value.unapply().concat(n.ann, env));
963 }
964 }
965 var branchCompilers = {
966 Fun: (len, ann, _, env, cont) -> {
967 var env2 = env.set({
968 argIdents: repeat(len, i -> [makeIdent('a' + i, here)])
969 });
970 letstx $name = unwrapSyntax(fnName) === 'anonymous' ? [] : fnName,
971 $args = join(makePunc(',', here), env2.argIdents),
972 $code = optimizeSyntax(cont(env2));
973 if (matchArgs.length) {
974 letstx $params = join(makePunc(',', here), matchArgs);
975 return #{
976 function $star $name ($args) { $code }.call(this, $params)
977 }
978 } else {
979 return #{
980 function $star $name ($args) { $code }
981 }
982 }
983 },
984 Match: (len, ann, _, env, cont) -> {
985 var bref = makeRef();
986 var args = matchArgs.reduce((acc, a) -> {
987 if (a.length === 1 && a[0].token.type === T.Identifier) {
988 acc[0].push(a);
989 } else {
990 var ref = makeRef();
991 acc[1] = makeAssign(null, ref[0], a).concat(acc[1]);
992 acc[0].push(ref);
993 }
994 return acc;
995 }, [[], []]);
996 var env2 = env.set({ argIdents: args[0], backtrackRef: bref });
997 var body = cont(env2);
998 letstx $top = args[1],
999 $ref = bref,
1000 $bod = optimizeSyntax(body);
1001 return #{
1002 var $ref = 1;
1003 $top
1004 $bod
1005 }
1006 },
1007 Args: compileNoop,
1008 Arg: (i, ann, _, env, cont) -> {
1009 return cont(env, env.argIdents[i]);
1010 },
1011 Unit: (ann, _, env, cont) -> {
1012 letstx $bod = cont(env);
1013 return #{
1014 if (arguments.length === 0) { $bod }
1015 }
1016 },
1017 Wild: compileNoop,
1018 Undef: (ann, ref, env, cont) -> {
1019 letstx $ref = ref,
1020 $bod = cont(env);
1021 return #{
1022 if ($ref === void 0) { $bod }
1023 }
1024 },
1025 Lit: (v, ann, ref, env, cont) -> {
1026 letstx $ref = ref,
1027 $lit = ann.stx,
1028 $bod = cont(env);
1029 return #{
1030 if ($ref === $lit) { $bod }
1031 }
1032 },
1033 Extractor: compileNoop,
1034 Inst: (ann, ref, env, cont) -> {
1035 if (natives.hasOwnProperty(ann.name)) {
1036 letstx $test = natives[ann.name](ref, env),
1037 $bod = cont(env);
1038 return #{
1039 if ($test) { $bod }
1040 }
1041 } else {
1042 letstx $ref = ref,
1043 $cls = ann.extractor,
1044 $bod = cont(env);
1045 return #{
1046 if ($cls.hasInstance
1047 ? $cls.hasInstance($ref)
1048 : $ref instanceof $cls) { $bod }
1049 }
1050 }
1051 },
1052 Unapply: (ann, ref, env, cont) -> {
1053 var ref2 = makeRef();
1054 letstx $ref = ref,
1055 $new = ref2,
1056 $ext = ann.extractor,
1057 $bod = cont(env, ref2);
1058 return #{
1059 var $new = $ext.unapply($ref);
1060 if ($new != null) { $bod }
1061 }
1062 },
1063 UnapplyObj: (ann, ref, env, cont) -> {
1064 var ref2 = makeRef();
1065 letstx $ref = ref,
1066 $new = ref2,
1067 $ext = ann.extractor,
1068 $bod = cont(env, ref2);
1069 return #{
1070 var $new = $ext.unapplyObject($ref);
1071 if ($new != null) { $bod }
1072 }
1073 },
1074 Arr: (ann, ref, env, cont) -> {
1075 letstx $test = natives.Array(ref, env),
1076 $bod = cont(env, ref);
1077 return #{
1078 if ($test) { $bod }
1079 }
1080 },
1081 Len: (len, ann, ref, env, cont) -> {
1082 letstx $len = [makeValue(len, here)],
1083 $ref = ref,
1084 $bod = cont(env);
1085 return #{
1086 if ($ref.length === $len) { $bod }
1087 }
1088 },
1089 LenMin: (len, ann, ref, env, cont) -> {
1090 letstx $len = [makeValue(len, here)],
1091 $ref = ref,
1092 $bod = cont(env);
1093 return #{
1094 if ($ref.length >= $len) { $bod }
1095 }
1096 },
1097 Index: (i, ann, ref, env, cont, bs) -> {
1098 var index = i >= 0
1099 ? [makeValue(i, here)]
1100 : ref.concat(makePunc('.', here),
1101 makeIdent('length', here),
1102 makePunc('-', here),
1103 makeValue(Math.abs(i), here));
1104 if (bs.length === 1 && bs[0].node.value.isWild) {
1105 if (!bs[0].node.ann.idents || !bs[0].node.ann.idents.length) {
1106 return cont(env);
1107 } else {
1108 return cont(env, ref.concat(makeDelim('[]', index, here)));
1109 }
1110 }
1111 var ref2 = makeRef();
1112 letstx $ref = ref,
1113 $ind = index,
1114 $new = ref2,
1115 $bod = cont(env, ref2);
1116 return #{
1117 var $new = $ref[$ind];
1118 $bod
1119 }
1120 },
1121 IndexNoop: compileNoop,
1122 Obj: (ann, ref, env, cont) -> {
1123 var ref2 = makeRef();
1124 letstx $ref = ref,
1125 $new = ref2,
1126 $bod = cont(env, ref2);
1127 return #{
1128 if ($ref != null) {
1129 var $new = Object($ref);
1130 $bod
1131 }
1132 }
1133 },
1134 KeyIn: (key, ann, ref, env, cont) -> {
1135 letstx $ref = ref,
1136 $key = [makeValue(key, here)],
1137 $bod = cont(env);
1138 if (ann.hasOwn) {
1139 return #{
1140 if ($ref.hasOwnProperty($key)) { $bod }
1141 }
1142 } else {
1143 return #{
1144 if ($key in $ref) { $bod }
1145 }
1146 }
1147 },
1148 KeyVal: (key, ann, ref, env, cont, bs) -> {
1149 if (bs.length === 1 && bs[0].node.value.isWild) {
1150 if (!bs[0].node.ann.idents || !bs[0].node.ann.idents.length) {
1151 return cont(env);
1152 } else {
1153 return cont(env, ref.concat(makeDelim('[]', [makeValue(key, here)], here)));
1154 }
1155 }
1156 var ref2 = makeRef();
1157 letstx $ref = ref,
1158 $new = ref2,
1159 $key = [makeValue(key, here)],
1160 $bod = cont(env, ref2);
1161 return #{
1162 var $new = $ref[$key];
1163 $bod
1164 }
1165 },
1166 KeyNoop: compileNoop,
1167 Rest: (pattern, names, ann, ref, env, cont) -> {
1168 var refs = ann.stashed.reduce((acc, id) -> {
1169 var k = unwrapSyntax(id.ident);
1170 if (!acc[2].hasOwnProperty(k)) {
1171 acc[0].push(id);
1172 acc[1].push(makeRef());
1173 acc[2][k] = true;
1174 }
1175 return acc;
1176 }, [[], [], {}]);
1177 var init = refs[1].length
1178 ? [makeKeyword('var', here)].concat(
1179 join(makePunc(',', here), refs[1].map(r -> {
1180 return r.concat(makePunc('=', here), makeDelim('[]', [], here));
1181 })),
1182 makePunc(';', here))
1183 : [];
1184 var oref = makeRef();
1185 var iref = makeRef();
1186 var sref = makeRef();
1187 var lref = makeRef();
1188 var aref = ann.argRest ? [makeIdent('arguments', here)] : ref;
1189 var start = [makeValue(ann.start, here)];
1190 var stop = withSyntax($a = aref) {
1191 if (!ann.stop) {
1192 return #{ $a.length };
1193 } else {
1194 letstx $stop = [makeValue(Math.abs(ann.stop), here)];
1195 return #{ $a.length - $stop };
1196 }
1197 };
1198 var end = Leaf(Ann(RestEnd(), { stashed: refs[0], refs: refs[1] }));
1199 var g = groupRows([[annotateLevels(pattern, 1)]], [[[end]]], 1)[0];
1200 var t = transformCase(g.node, g.matrix, g.stack, 1);
1201 var s = compilePattern(t, environment({ refs: {} }), [void 0, lref]);
1202 var env2 = ann.stashed.reduce((e, id, i) -> {
1203 return e.stash(unwrapSyntax(id.ident), refs[1][i]);
1204 }, env);
1205 letstx $init = init,
1206 $oref = oref,
1207 $aref = aref,
1208 $iref = iref,
1209 $lref = lref,
1210 $sref = sref,
1211 $start = start,
1212 $stop = stop,
1213 $inner = s,
1214 $bod = cont(env2);
1215 return #{
1216 $init
1217 var $oref = 1;
1218 for (var $iref = $start, $sref = $stop, $lref; $iref < $sref; $iref++) {
1219 $lref = $aref[$iref];
1220 $inner
1221 $oref--;
1222 break;
1223 }
1224 if ($oref) { $bod }
1225 }
1226 },
1227 Guard: (ann, _, env, cont) -> {
1228 var names = ann.stashed.reduce((acc, id) -> {
1229 var k = unwrapSyntax(id.ident);
1230 acc[k] = env.retrieve(k);
1231 return acc;
1232 }, {});
1233 letstx $test = replaceIdents(ann.stx, names),
1234 $bod = cont(env);
1235 return #{
1236 if ($test) { $bod }
1237 }
1238 },
1239 Backtrack: (ann, _, env, cont) -> {
1240 letstx $bod = cont(env),
1241 $ref = env.backtrackRef;
1242 return #{
1243 if ($ref) { $bod }
1244 }
1245 }
1246 };
1247 var leafCompilers = {
1248 Body: (ann, env) -> {
1249 var refs = join([], ann.stashed.map(id -> {
1250 return makeAssign(id.keyword, id.ident, env.retrieve(unwrapSyntax(id.ident)));
1251 }));
1252 letstx $bod = refs.concat(ann.stx),
1253 $user = [makeIdent(USERCODE.value, here)];
1254 if (matchStmt && !ann.last) {
1255 letstx $ref = env.backtrackRef;
1256 return #{
1257 if ($ref--) { $user { $bod } }
1258 }
1259 } else {
1260 return #{
1261 $user { $bod }
1262 }
1263 }
1264 },
1265 RestEnd: (ann, env) -> {
1266 var refs = join([], ann.stashed.map((id, i) -> {
1267 letstx $arr = ann.refs[i],
1268 $ref = env.retrieve(unwrapSyntax(id.ident));
1269 return #{
1270 $arr[$arr.length] = $ref;
1271 }
1272 }));
1273 letstx $refs = refs;
1274 return #{
1275 $refs
1276 continue;
1277 }
1278 },
1279 NoMatch: (ann, env) -> {
1280 return #{
1281 throw new TypeError('No match');
1282 }
1283 }
1284 };
1285 function compileNoop() {
1286 var cont = arguments[arguments.length - 2];
1287 var env = arguments[arguments.length - 3];
1288 return cont(env);
1289 }
1290 var natives = {
1291 Boolean : typeofAndObjTag('boolean', 'Boolean'),
1292 Number : typeofAndObjTag('number', 'Number'),
1293 String : typeofAndObjTag('string', 'String'),
1294 Function : typeofAndObjTag('function', 'Function'),
1295 RegExp : objTag('RegExp'),
1296 Date : objTag('Date'),
1297 Math : objTag('Math'),
1298 Object : objTag('Object'),
1299 Array: (ref, env) -> {
1300 letstx $ref = ref;
1301 return #{ Array.isArray
1302 ? Array.isArray($ref)
1303 : Object.prototype.toString.call($ref) === '[object Array]' };
1304 },
1305 NaN: (ref, env) -> {
1306 letstx $ref = ref;
1307 return #{ Number.isNaN
1308 ? Number.isNaN($ref)
1309 : typeof $ref === 'number' && $ref !== +$ref };
1310 }
1311 }
1312 function typeofAndObjTag(type, tag) {
1313 return (ref, env) -> {
1314 letstx $type = [makeValue(type, here)],
1315 $str = [makeValue('[object ' + tag + ']', here)],
1316 $ref = ref;
1317 return #{ typeof $ref === $type ||
1318 Object.prototype.toString.call($ref) === $str };
1319 }
1320 }
1321 function objTag(tag) {
1322 return (ref, env) -> {
1323 letstx $str = [makeValue('[object ' + tag + ']', here)],
1324 $ref = ref;
1325 return #{ Object.prototype.toString.call($ref) === $str };
1326 }
1327 }
1328
1329 return compile(parse(#{ $body ... }));
1330 }
1331}
1332
1333macro star {
1334 rule { * }
1335 rule {}
1336}
1337
1338let function = macro {
1339 case { $ctx $star:star $name:ident { $body ... } } => {
1340 return #{
1341 compile $ctx $star $name () { $body ... }
1342 };
1343 }
1344 case { $ctx $star:star { $body ... } } => {
1345 return #{
1346 compile $ctx $star anonymous () { $body ... }
1347 }
1348 }
1349 case { _ } => {
1350 return #{ function }
1351 }
1352}
1353
1354let match = macro {
1355 case { $ctx ($op:expr, $rest:expr (,) ...) { $body ... } } => {
1356 return #{
1357 compile $ctx anonymous (($op) $(($rest)) ...) { $body ... }
1358 }
1359 }
1360 case { $ctx ($op:expr) { $body ... } } => {
1361 return #{
1362 compile $ctx anonymous (($op)) { $body ... }
1363 }
1364 }
1365 case { $ctx $op:expr { $body ... } } => {
1366 return #{
1367 compile $ctx anonymous (($op)) { $body ... }
1368 }
1369 }
1370 case { _ } => {
1371 return #{ match }
1372 }
1373}
1374
1375export function;
1376export match;
1377
\No newline at end of file