-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
64 lines (51 loc) · 2.33 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const isAtomic = require(`./is-atomic.js`)
const regexSource = require(`./regex-source.js`)
const combine = returnsRegex((...args) => escapeInputForCombining(...args).join(``))
const guaranteeAtomic = regex => isAtomic(regex) ? regex : `(?:${regexSource(regex)})`
const escapeRegex = str => str.replace(/[.?*+^$[\]\\(){}|-]/g, `\\$&`)
const ifRegex = (input, ifCase, elseIfCase) => input instanceof RegExp ? ifCase(input) : elseIfCase(input)
const escapeInputAndReturnString = regex => ifRegex(regex, regex => regex.source, escapeRegex)
module.exports = {
combine,
either: makeJoiningFunction(`(?:`, `|`, `)`),
capture: makeJoiningFunction(`(`, ``, `)`),
flags: (flags, ...args) => new RegExp(combine(...args).source, flags),
anyNumber: suffix(`*`),
oneOrMore: suffix(`+`),
optional: suffix(`?`),
exactly: (n, ...regexes) => suffix(`{${n}}`)(...regexes),
atLeast: (n, ...regexes) => suffix(`{${n},}`)(...regexes),
between: (n, m, ...regexes) => suffix(`{${n},${m}}`)(...regexes),
anyNumberNonGreedy: suffix(`*?`),
oneOrMoreNonGreedy: suffix(`+?`),
optionalNonGreedy: suffix(`??`),
exactlyNonGreedy: (n, ...regexes) => suffix(`{${n}}?`)(...regexes),
atLeastNonGreedy: (n, ...regexes) => suffix(`{${n},}?`)(...regexes),
betweenNonGreedy: (n, m, ...regexes) => suffix(`{${n},${m}}?`)(...regexes),
}
function removeNonCapturingGroupIfExists(regexString) {
const match = /^\(\?:(.+)\)$/.exec(regexString)
return match ? match[1] : regexString
}
function guaranteeNoTopLevelOrs(regexString) {
return regexString.indexOf(`|`) >= 0 ? guaranteeAtomic(regexString) : regexString
}
function escapeInputForCombining(...args) {
return args.map(escapeInputAndReturnString).map(guaranteeNoTopLevelOrs)
}
function returnsRegex(fn) {
return (...args) => ifRegex(fn(...args), regex => regex, input => new RegExp(input))
}
function makeJoiningFunction(openingCharacter, joinCharacter, closingCharacter) {
return returnsRegex((...args) => {
const naiveBody = escapeInputForCombining(...args).join(joinCharacter)
const body = isAtomic(naiveBody) ? removeNonCapturingGroupIfExists(naiveBody) : naiveBody
return concat(openingCharacter, body, closingCharacter)
})
}
function suffix(appendCharacter) {
return returnsRegex((...args) => concat(guaranteeAtomic(combine(...args)), appendCharacter))
}
function concat(...regexes) {
return regexes.map(regexSource).join(``)
}