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