1import { astRoot, _private as privateId } from './codegen';
2import { parse as parseDSL } from './parser';
3
4const isStickySupported = typeof /./g.sticky === 'boolean';
5
6export const __private = {
7 pattern(input) {
8 if (typeof input === 'function') return input;
9 const source = typeof input !== 'string' ? input.source : input;
10 return isStickySupported
11 ? new RegExp(source, 'y')
12 : new RegExp(source + '|()', 'g');
13 },
14
15 exec(state, pattern) {
16 let match;
17
18 if (typeof pattern === 'function') {
19 if (!pattern.length) pattern = pattern();
20 return pattern(state);
21 }
22
23 const input = state.quasis[state.x];
24 if (input && (pattern.lastIndex = state.y) < input.length) {
25 if (isStickySupported) {
26 if (pattern.test(input))
27 match = input.slice(state.y, pattern.lastIndex);
28 } else {
29 match = pattern.exec(input)[0] || match;
30 }
31
32 state.y = pattern.lastIndex;
33 }
34
35 return match;
36 },
37};
38
39export const interpolation = (predicate) => (state) => {
40 let match;
41
42 if (
43 state.y >= state.quasis[state.x].length &&
44 state.x < state.expressions.length
45 ) {
46 state.y = 0;
47 match = state.expressions[state.x++];
48 if (predicate && match) match = predicate(match);
49 }
50
51 return match;
52};
53
54export const parse = (matcher) => (quasis, ...expressions) => {
55 if (typeof quasis === 'string') quasis = [quasis];
56 const state = { quasis, expressions, x: 0, y: 0 };
57 return matcher(state);
58};
59
60export const match = (name, transform) => (quasis, ...expressions) => {
61 const ast = parseDSL(
62 quasis,
63 expressions.map((expression, i) => ({
64 fn: typeof expression === 'function' && expression.length,
65 id: `_${i}`,
66 }))
67 );
68
69 const makeMatcher = new Function(
70 privateId +
71 ',_n,_t,' +
72 expressions.map((_expression, i) => `_${i}`).join(','),
73 'return ' + astRoot(ast, '_n', transform ? '_t' : null)
74 );
75
76 return makeMatcher(
77 __private,
78 name,
79 transform,
80 ...expressions.map(__private.pattern)
81 );
82};