UNPKG

incr-regex-package

Version:

An incremental regular expression parser in JavaScript; useful for input validation, RegExp

599 lines (500 loc) 16.7 kB
/** * Copyright (c) 2016, Nurul Choudhury * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ "use strict"; // export function assign(object) { // if (!object) { // return object; // } // for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { // var iterable = arguments[argsIndex]; // if (typeof iterable === 'function' || (typeof iterable === 'object' && iterable !== null)) { // var index = -1, keys = Object.keys(iterable), // length = keys ? keys.length : 0, prop; // while (++index < length) { // prop = keys[index]; // object[prop] = iterable[prop]; // } // } // } // return object; // } export function assign(...object) { return Object.assign(...object)} export function copy(obj) { return assign({},obj); } //function has(obj,key) { return !!obj[key]; } // export function extend(protoProps, staticProps) { // var Parent = this, Child; // if (typeof Parent !== 'function') throw new Error('Parent must be a constructor function'); // if (has(protoProps, 'constructor')) { // Child = protoProps.constructor; // } else { // Child = function(){ return Parent.apply(this, arguments); }; // } // assign(Child, Parent, staticProps); // // subclass extends superclass // Child.prototype = Object.create(Parent.prototype); // if (protoProps) assign(Child.prototype, protoProps); // Child.prototype.constructor = Child; // //Child.__super__ = Parent.prototype; // return Child; // } export const contract = (function() { const call = Function.prototype.call; const getClassName = call.bind({}.toString); // A contract that allows anything const isUndef = function(x) { return (x === undefined); }; const NVL= function(v,dflt) { return isUndef(v)? dflt: v; }; const any = function(x) { return x; }; const isClassOf = function(s) { var TYPE = "[object " + s + "]"; return function(v) { return (getClassName(v) == TYPE); }; }; const classOf = function(s) { var TYPE = "[object " + s + "]"; return function(v) { if (getClassName(v) !== TYPE) { throw new TypeError("Expected " + s); } return v; }; }; const isArr = isClassOf("Array"); // Manditory contract const arr = classOf("Array"); const isTypeOf = function(s) { return function(v) { return (typeof v == s); }; }; // Creates a contract for a value of type s const typeOf = function(s) { return function(v) { if (typeof v !== s) { throw new TypeError("Expected a" + (s === "object" ? "n" : "") + s + "."); } return v; }; }; //Manditory contract const func = typeOf("function"); const isFunc = isTypeOf("function"); const STR = typeOf('String'); // Creates a contract for an object inheriting from ctor const instanceOf = function(ctor) { return function(inst) { if (!(inst instanceof ctor)) { throw new TypeError("Expected an instance of " + ctor); } return inst; }; }; const int32 = function(n) { if ((n | 0) !== n ) { throw new TypeError("Expected a 32-bit natural."); } return n; }; // Asserts int32 and nonnegative const nat32 = function(n) { if ((n | 0) !== n || n < 0) { throw new TypeError("Expected a 32-bit natural."); } return n; }; return { int32, nat32, func, isFunc, typeOf, isTypeOf, arr, isArr, classOf, isClassOf, instanceOf, isUndef, STR, any, NVL }; })(); export function ID(x) { return x; } export function flatten(anArray) { var newArray = []; return newArray.concat.apply(newArray, anArray); } export function array_eq(array, another) { // if the other array is a falsy value, return if( array === another ) return true; if (!array || !another) return false; // compare lengths - can save a lot of time if (another.length !== array.length) return false; for (let i = 0, l=another.length; i < l; i++) { // Check if we have nested arrays if (another[i] instanceof Array && array[i] instanceof Array) { // recurse into the nested arrays if (!array_eq(array[i], another[i])) return false; } else if (another[i] !== array[i]) { // Warning - two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; } export function arr_uniq(arr) { var seen = {}; var arr2 = []; for (var i = 0; i < arr.length; i++) { if (!(arr[i] in seen)) { arr2.push(arr[i]); seen[arr[i]] = true; } } return arr2; } export function arr_find(fn,arr) { for (var i = 0; i < arr.length; i++) { if (fn(arr[i])) { return arr[i]; } } return undefined; } export function array_match(array, subArray, at) { // if the other array is a falsy value, return if (!array || !subArray) return false; let len = array.length; let lenS = subArray.length; if( at+lenS > len ) return false; // cannot match subArray too long for (let i = at, j=0; j < lenS; i++,j++) { // Check if we have nested arrays if (subArray[j] instanceof Array && array[i] instanceof Array) { // recurse into the nested arrays if (!array_match(array[i], subArray[j],0)) return false; } else if (array[i] !== subArray[j]) { // Warning - two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; } export function array_append(arr, list) { for(let i=0; i< list.length; i++) arr.push(list[i]); return arr; } // Handy string utilities export function sreverse(s) { var o = []; for (var i = 0, len = s.length; i <= len; i++) o.push(s.charAt(len - i)); return o.join(''); } // Find the prefix of two strings, // s1 = prefix + rest_of_s1; // s2 = prefix + rest_of_s2 // sprefix(s1,s2) === prefix export function sprefix(s1,s2) { let i = 0; for(; i< s1.length && i < s2.length; i++) { if(s1.charAt(i) !== s2.charAt(i)) return s1.substring(0,i); } return s1.length < s2.length?s1:s2; } // Find the postfix of two strings, // s1 = s1_start + post; // s2 = s2_start + post; // rprefix(s1,s2) === post export function rprefix(s1,s2) { return sreverse(sprefix(sreverse(s1),sreverse(s2))); } // s = head + rest_of_s // n = head.length // shead(s,n) === head export function shead(s,n) { return s.length < n ? s : s.substr(0,n); } // s = start_os_s + tail // n = tail.length // stail(s,n) === tail export function stail(s,n) { return s.length < n ? s : s.substr(s.length-n,n); } // s1 = start_of_s1 + end // s2 = end + rest_of_s2 // sRightMerge(s1,s2) === start_of_s1 + s2 // example s1 = "Jim hello", s2 = "hello how are you" // sRightMerge(s1,s2) === "Jim hello how are you" // where: end is the longet string that satisfies the relationship above export function sRightMerge(s1,s2) { let n = Math.min(s1.length,s2.length); function match(s1,s2,n) { for(let i=s1.length-n, j=0; j<n; i++, j++) if( s1.charAt(i) !== s2.charAt(j)) return false; return true; } for(let i = n; i>0; i--) { if(match(s1,s2,i)) return s1.substr(0,s1.length-i)+s2; } return s1+s2; } // Polyfill Object.assign if (typeof Object.assign != 'function') { (function () { Object.assign = function (target) { if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; })(); } // function rxtokensOld() { // var t = "\\[(?:\\\\]|[^\\]]])*\\]"; // var meta = "[.\\]|)]|\\(\\?:|\\(|\\?\\?|\\?|\\*\\?|\\*|\\+\\?|\\+"; // var escaped = "\\\\(?:"+meta + "|" + "[dDsSbBwW\\[{}\\]])"; // var group ="\\{[0-9]+(?:,[0-9]*)?\\}"; // var nonMeta = "[^.+?{}\\]\\[|()]"; // return [ /*unicode,*/ t, group, escaped, meta, nonMeta ].join("|"); // } /* REGEXP TOKENIZER HELPERS */ export const TOKINIZATION_RX = makeRegexp(); function rxtokens() { function ESACPE(s) { return s.split('').map( a => "\\"+a).join('|'); } function OR(s) { return s.split('').join('|'); } //var charSet = "\\[(?:\\\\u|\\\\\\]|\\\\\\\\|(\\\\)?\\[|[^\\]\\[\\\\])*?\\]"; var charSet = "(?:\\[\\^\\]|\\[\\^?(?:\\\\.|[^\\]])*?(?:\\\\\\\\]|[^\\\\]]))"; // may not work //var meta = "[.\\]|)]|\\(\\?:|\\(|\\?\\?|\\?|\\*\\?|\\*|\\+\\?|\\+"; var meta1 = ESACPE(".|+*?()^$") var meta2 = "\\(\\?:|\\?\\?|\\*\\?|\\+\\?"; var escapeTarget1 = OR("dDsSbBwW"); var escapeTarget2 = ESACPE("[]{}\\"); var escapeTarget = [meta1,escapeTarget1,escapeTarget2].join('|') var escaped = "\\\\(?:"+ escapeTarget + ")"; var group ="\\{(?:\\d+,\\d+|\\d+|\\d+,|,\\d+)\\}"; var nonMeta = "[^.+?{}\\]\\[|()\\\\]"; return [ charSet, group, escaped, meta2, meta1, nonMeta ].map(a => "(?:"+a+")" ).join("|"); } // function makeRegexp() { return new RegExp( rxtokens() ,"g"); } export function parseMulti(str) { var m = str.match(/\{(\d+)(,(\d*))?\}/); // handles { 3 }, { 4, }, {6, 9} var low=Number(m[1]); return { min: Number(m[1]), max: (m[3]?Number(m[3]):(m[2]?undefined:low)) }; } export function odd(x) { return ((+x)&1) > 0; } // push element into an array, or return the element export function arr_push(arr, elem) { if( arr === undefined ) return elem; arr.push(elem); return arr; } // =========================== // Stack/Array with no duplicates export class StackDedup { constructor(v) { this.length = 0; this.data = []; v && this.push(v); this.maxLen = 0|0; } forEach(f) { let data = this.data; for(let i =0; i<this.length; i++) { f(data[i],i,this); } return this; } reduce(f,iniV) { let data = this.data; for(let i =0; i<this.length; i++) { iniV = f(iniV,data[i],i,this); } return iniV; } filter(f) { let s = new StackDedup(); let data = this.data; s.maxLen = this.maxLen; for(let i =0; i<this.length; i++) { if(f(data[i],i,this)) s.push(data[i]); } return s; } map(f) { let s = new StackDedup(); let data = this.data; s.maxLen = this.maxLen; for(let i =0; i<this.length; i++) { s.push(f(data[i],i,this)); } return s; } toArray() { return this.reduce( arr_push, []); } reset() { this.length = 0|0; this.maxLen = 0|0; return this; } contains(v) { let data = this.data; let len = this.length; for(let i=0; i<len; i++) { if(data[i] === v) { return this; } } return undefined; } push(v) { if( v === undefined ) return this; if( this.contains(v) ) return this; this.data[this.length++] = v; if( this.length > this.maxLen) this.maxLen = this.length; return this; } pop() { if(this.length <= 0) return this; this.length--; return this; } top() { if(this.length <= 0) return undefined; return this.data[this.length-1]; } addAll(list) { //console.log("ADD ALL",list); if(!list) return this; list.forEach( (a) => this.push(a) ); return this; } clone() { return this.map(a => a); } } //============================================ // Immutable list implementation class List_n { constructor(l,r) { this.head = l; this.tail = r; } equals(b) { if( b === null) return false; return this.head === b.head && this.tail === b.tail; } addAll(aList) { if( !aList ) return this; return n_reverse(aList).reduce( (rst, head) => n_cons(head, rst), this); } map(f) { return n_map(f,this); } reduce(f,base) { return n_reduce(f,this, base); } reverse(base) { return n_reverse(this,base)} } export function n_cons(elem, list,base) { return new List_n( elem, list ); } export function n_head(list) { if (!list) throw Error("n_head of empty list"); return list.head; } export function n_tail(list) { if (!list) throw Error("n_tail of empty list"); return list.tail; } /* note nl is optional */ export function n_reverse(list, nl=null) { //nl = nl || null; if (!list) return nl; return n_reverse(n_tail(list), n_cons(n_head(list), nl)); // simple tail recursion } export function arrayToList(array) { return n_reverse(array.reduce( (a, b) => n_cons(b, a), null)); } export function stringToList(str) { return arrayToList(str.split("")); } export function listToArray(list) { return n_reduce((a, b) => { a.push(b); return a; },list, []); } export function listToString(list) { return listToArray(list).join(''); } export function n_concat(list1, list2) { if (!list1) return list2; return (!list1) ? list2 : n_cons(n_head(list1), n_concat(n_tail(list1), list2)); } export function n_map(fn, list, i) { i = i || 0; return !list ? list : n_cons(fn(n_head(list), i, list), n_map(fn, n_tail(list), i + 1)); } export function n_filter(fn, list, i) { if (!list) return list; i = i || 0; if (fn(n_head(list), i, list)) return n_cons(n_head(list), n_filter(fn, n_tail(list), i + 1)); return n_filter(fn, n_tail(list), i + 1); } export function n_find(fn,list,i) { i = i || 0; if( !list ) return null; return fn(n_head(list),i,list) ? list : n_find(fn, n_tail(list), i+1); } export function n_reduce(fn, list, base) { return (!list) ? base : n_reduce(fn, n_tail(list), fn(base, n_head(list))); // simple tail recursion } export function n_removeAll(eq, list, listOfItemsToRemove) { if (arguments.length === 2) { listOfItemsToRemove = list; list = eq; eq = (a, b) => a === b; } return n_filter((e) => !n_find((a) => eq(a ,e), listOfItemsToRemove), list ); } // POlyfill for array.find if (!Array.prototype.find) { Array.prototype.find = function(predicate) { 'use strict'; if (this == null) { throw new TypeError('Array.prototype.find called on null or undefined'); } if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } var list = Object(this); var length = list.length >>> 0; var thisArg = arguments[1]; var value; for (var i = 0; i < length; i++) { value = list[i]; if (predicate.call(thisArg, value, i, list)) { return value; } } return undefined; }; } // stringToList, listToArray, listToString, n_cons, n_head, n_tail, n_filter, n_reduce, n_map, n_concat, n_removeAll