UNPKG

luhn-generator

Version:

A generator of numbers that passes the validation of Luhn algorithm or Luhn formula, also known as the 'modulus 10' or 'mod 10' algorithm

387 lines (310 loc) 7.63 kB
'use strict'; var regexNot = require('regex-not'); var toRegex = require('to-regex'); /** * Characters to use in negation regex (we want to "not" match * characters that are matched by other parsers) */ var cached; var NOT_REGEX = '[\\[!*+?$^"\'.\\\\/]+'; var not = createTextRegex(NOT_REGEX); /** * Nanomatch parsers */ module.exports = function(nanomatch, options) { var parser = nanomatch.parser; var opts = parser.options; parser.state = { slashes: 0, paths: [] }; parser.ast.state = parser.state; parser /** * Beginning-of-string */ .capture('prefix', function() { if (this.parsed) return; var m = this.match(/^\.[\\/]/); if (!m) return; this.state.strictOpen = !!this.options.strictOpen; this.state.addPrefix = true; }) /** * Escape: "\\." */ .capture('escape', function() { if (this.isInside('bracket')) return; var pos = this.position(); var m = this.match(/^(?:\\(.)|([$^]))/); if (!m) return; return pos({ type: 'escape', val: m[2] || m[1] }); }) /** * Quoted strings */ .capture('quoted', function() { var pos = this.position(); var m = this.match(/^["']/); if (!m) return; var quote = m[0]; if (this.input.indexOf(quote) === -1) { return pos({ type: 'escape', val: quote }); } var tok = advanceTo(this.input, quote); this.consume(tok.len); return pos({ type: 'quoted', val: tok.esc }); }) /** * Negations: "!" */ .capture('not', function() { var parsed = this.parsed; var pos = this.position(); var m = this.match(this.notRegex || /^!+/); if (!m) return; var val = m[0]; var isNegated = (val.length % 2) === 1; if (parsed === '' && !isNegated) { val = ''; } // if nothing has been parsed, we know `!` is at the start, // so we need to wrap the result in a negation regex if (parsed === '' && isNegated && this.options.nonegate !== true) { this.bos.val = '(?!^(?:'; this.append = ')$).*'; val = ''; } return pos({ type: 'not', val: val }); }) /** * Dot: "." */ .capture('dot', function() { var parsed = this.parsed; var pos = this.position(); var m = this.match(/^\.+/); if (!m) return; var val = m[0]; this.state.dot = val === '.' && (parsed === '' || parsed.slice(-1) === '/'); return pos({ type: 'dot', dotfiles: this.state.dot, val: val }); }) /** * Plus: "+" */ .capture('plus', /^\+(?!\()/) /** * Question mark: "?" */ .capture('qmark', function() { var parsed = this.parsed; var pos = this.position(); var m = this.match(/^\?+(?!\()/); if (!m) return; this.state.metachar = true; this.state.qmark = true; return pos({ type: 'qmark', parsed: parsed, val: m[0] }); }) /** * Globstar: "**" */ .capture('globstar', function() { var parsed = this.parsed; var pos = this.position(); var m = this.match(/^\*{2}(?![*(])(?=[,)/]|$)/); if (!m) return; var type = opts.noglobstar !== true ? 'globstar' : 'star'; var node = pos({type: type, parsed: parsed}); this.state.metachar = true; while (this.input.slice(0, 4) === '/**/') { this.input = this.input.slice(3); } node.isInside = { brace: this.isInside('brace'), paren: this.isInside('paren') }; if (type === 'globstar') { this.state.globstar = true; node.val = '**'; } else { this.state.star = true; node.val = '*'; } return node; }) /** * Star: "*" */ .capture('star', function() { var pos = this.position(); var starRe = /^(?:\*(?![*(])|[*]{3,}(?!\()|[*]{2}(?![(/]|$)|\*(?=\*\())/; var m = this.match(starRe); if (!m) return; this.state.metachar = true; this.state.star = true; return pos({ type: 'star', val: m[0] }); }) /** * Slash: "/" */ .capture('slash', function() { var pos = this.position(); var m = this.match(/^\//); if (!m) return; this.state.slashes++; return pos({ type: 'slash', val: m[0] }); }) /** * Backslash: "\\" */ .capture('backslash', function() { var pos = this.position(); var m = this.match(/^\\(?![*+?(){}[\]'"])/); if (!m) return; var val = m[0]; if (this.isInside('bracket')) { val = '\\'; } else if (val.length > 1) { val = '\\\\'; } return pos({ type: 'backslash', val: val }); }) /** * Square: "[.]" */ .capture('square', function() { if (this.isInside('bracket')) return; var pos = this.position(); var m = this.match(/^\[([^!^\\])\]/); if (!m) return; return pos({ type: 'square', val: m[1] }); }) /** * Brackets: "[...]" (basic, this can be overridden by other parsers) */ .capture('bracket', function() { var pos = this.position(); var m = this.match(/^(?:\[([!^]?)([^\]]+|\]-)(\]|[^*+?]+)|\[)/); if (!m) return; var val = m[0]; var negated = m[1] ? '^' : ''; var inner = (m[2] || '').replace(/\\\\+/, '\\\\'); var close = m[3] || ''; if (m[2] && inner.length < m[2].length) { val = val.replace(/\\\\+/, '\\\\'); } var esc = this.input.slice(0, 2); if (inner === '' && esc === '\\]') { inner += esc; this.consume(2); var str = this.input; var idx = -1; var ch; while ((ch = str[++idx])) { this.consume(1); if (ch === ']') { close = ch; break; } inner += ch; } } return pos({ type: 'bracket', val: val, escaped: close !== ']', negated: negated, inner: inner, close: close }); }) /** * Text */ .capture('text', function() { if (this.isInside('bracket')) return; var pos = this.position(); var m = this.match(not); if (!m || !m[0]) return; return pos({ type: 'text', val: m[0] }); }); /** * Allow custom parsers to be passed on options */ if (options && typeof options.parsers === 'function') { options.parsers(nanomatch.parser); } }; /** * Advance to the next non-escaped character */ function advanceTo(input, endChar) { var ch = input.charAt(0); var tok = { len: 1, val: '', esc: '' }; var idx = 0; function advance() { if (ch !== '\\') { tok.esc += '\\' + ch; tok.val += ch; } ch = input.charAt(++idx); tok.len++; if (ch === '\\') { advance(); advance(); } } while (ch && ch !== endChar) { advance(); } return tok; } /** * Create text regex */ function createTextRegex(pattern) { if (cached) return cached; var opts = {contains: true, strictClose: false}; var not = regexNot.create(pattern, opts); var re = toRegex('^(?:[*]\\((?=.)|' + not + ')', opts); return (cached = re); } /** * Expose negation string */ module.exports.not = NOT_REGEX;