incr-regex-package
Version:
An incremental regular expression parser in JavaScript; useful for input validation, RegExp
314 lines (294 loc) • 9.96 kB
JavaScript
/**
* Copyright (c) 2016..2020, 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";
//import { assign, copy, extend } from "./utils";
//import { incrRegEx } from "./incr-regex-v3";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RxMatcher = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _regexUtils = require("./regex-utils");
var _rxtree = require("./rxtree");
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
// array of [ [type, ch] ...]
// function hasMetaChars(tracker) {
// return tracker.find(([, ch]) => isHolder(ch));
// }
function _fixTracker(tracker, i) {
var elem = tracker[i];
if (elem[1] !== undefined && elem[0] !== elem[1]) {
elem[0] = elem[1];
//console.log(`fixTracker: set Value at: ${i} to '${elem[1]}'`)
}
//else console.log(`fixTracker: no action at: ${i} val: '${elem[0]}' state:'${elem[1]}'`)
}
var RxMatcher = exports.RxMatcher = function () {
function RxMatcher(matcher) {
_classCallCheck(this, RxMatcher);
this.matcher = matcher;
this._lastEditableIndex = undefined;
//this._tracker;
}
_createClass(RxMatcher, [{
key: "getFirstEditableAtOrAfter",
value: function getFirstEditableAtOrAfter(ix) {
var i = ix;
var tracker = this.getInputTracker();
for (; i < tracker.length; i++) {
if (tracker[i][1] === undefined) return i;else _fixTracker(tracker, i);
} // make sure we fixup the value of fixed values
var m = this.minChars();
i = tracker.length;
var j = 0;
for (; j < m.length && !(0, _regexUtils.isMeta)(m.charAt(j)); j++, i++) {}
return i;
}
// we we find a position that has aholder, but should be a fixed characted
// convert the older to a fixed character
}, {
key: "fixTracker",
value: function fixTracker() {
var tracker = this.getInputTracker();
for (var i = 0; i < tracker.length; i++) {
_fixTracker(tracker, i);
}
}
}, {
key: "getFirstEditableAtOrBefore",
value: function getFirstEditableAtOrBefore(ix) {
var tracker = this.getInputTracker();
if (ix >= tracker.length) ix = tracker.length - 1;
for (; ix > 0; ix--) {
if (tracker[ix][1] === undefined) return ix;else _fixTracker(tracker, ix);
}return 0;
}
}, {
key: "getInputLength",
value: function getInputLength() {
return this.matcher.getInputLength();
}
/*
This code will use back propogation, that given input, for example
input: xxx______________yyy
patter: p
fill in the unknows, kind of algebra problem
suppose p = /xxx\d.*fred\d{9}yyy|xxx.joy.*zzz/
input must be: xxx_fred_________yyy
nothing else will fit.
This is a tricky problem, I am not confident i have a proveably
correct algorithm for this to handle all edge cases, but the
back propogation method will handle most cases
*/
}, {
key: "updateFixed",
value: function updateFixed() {
//console.log(`updateFixed: ${start}, ${end} not yet implemented`);
//this.fixTracker();
//if( !hasMetaChars(this.getInputTracker())) return false;
var s = this.matcher.inputStr();
s = (0, _rxtree.rxContentsToMask)(this.matcher.base, s);
if (s != undefined) {
this.matcher.reset();
this.matcher.matchStr(s);
return true;
}
return false;
}
}, {
key: "setPos",
value: function setPos(ix) {
//let currTracker = this.tracker.slice(0); // copy the array
if (ix !== undefined) {
var tracker = this.getInputTracker();
var s = tracker.map(function (s) {
return s[0];
}).join('').substr(0, ix);
this.reset();
this.matchStr(s);
tracker = this.getInputTracker(); // have to do this since we did some matching
for (; tracker.length < ix && this.fixed() !== undefined;) {
if (!this.match(this.fixed())) {
ix = this.getInputLength();
break;
}
} //this._mask = undefined;
this._resetCache();
}
return ix;
}
}, {
key: "toString",
value: function toString() {
return this.matcher.toString();
} /* public */
}, {
key: "after",
value: function after(ix) {
return this.matcher._after(true, ix);
}
}, {
key: "valueWithMask",
value: function valueWithMask() {
return this.matcher.valueWithMask();
}
}, {
key: "rawValue",
value: function rawValue(ix) {
return this.matcher.rawValue(ix);
}
}, {
key: "_after",
value: function _after(flag, ix) {
return this.matcher._after(flag, ix);
}
}, {
key: "isDone",
value: function isDone(ix) {
return this.matcher.isDone(ix);
}
}, {
key: "setToFirstEditableAfter",
value: function setToFirstEditableAfter(ix) {
if (ix === undefined) ix = this.getInputLength();
return this.setPos(this.getFirstEditableAtOrAfter(ix));
}
}, {
key: "lastEditableIndex",
value: function lastEditableIndex() {
if (this._lastEditableIndex === undefined) {
var tracker = this.getInputTracker();
var rx = this.clone();
var ix = this.getFirstEditableAtOrAfter(tracker.length);
rx.setPos(ix);
if (rx.state() === _rxtree.DONE) ix = tracker.length;
this._lastEditableIndex = ix;
}
return this._lastEditableIndex;
}
}, {
key: "getTree",
value: function getTree() {
return this.matcher.getTree();
} /* public */ // Get the parse tree from the regular expression
}, {
key: "minChars",
value: function minChars(ix) {
/* public */ // get a ask for the regular expression from the current state of the match
if (ix === undefined) return this.matcher.minChars();
var p = this.matcher.clone();
var s = this.matcher._after(true, 0).substring(0, ix);
p.reset();
var ret = p.matchStr(s);
//console.log("ix: ",ix, " str: '", s,"'");
if (!ret[0]) {
throw new Error("Unexpected error (matchStr failed) from " + p.constructor.name || "IREGEX");
}
return p.minChars();
}
}, {
key: "minCharsList",
value: function minCharsList(flag) {
//if( !flag ) throw new Error("flag should be true");
return this.matcher.minCharsList(flag);
}
}, {
key: "emptyAt",
value: function emptyAt(ix) {
var tracker = this.getInputTracker();
if (ix < tracker.length) return (0, _regexUtils.isHolder)(tracker[ix][0]);
return false;
}
}, {
key: "match",
value: function match(ch) {
/* public */
this._resetCache();
var ret = this.matcher.match(ch);
this.fixTracker();
return ret;
}
}, {
key: "matchStr",
value: function matchStr(str) {
/* public */
this._resetCache();
var ret = this.matcher.matchStr(str);
this.fixTracker();
return ret;
}
}, {
key: "state",
value: function state() {
/* public */
return this.matcher.state();
}
}, {
key: "fixed",
value: function fixed() {
return this.matcher.fixed();
}
}, {
key: "reset",
value: function reset() {
/* public */
this.matcher.reset();
this._resetCache();
return this;
}
}, {
key: "clone",
value: function clone() {
return new RxMatcher(this.matcher.clone());
}
}, {
key: "getInputTracker",
value: function getInputTracker() {
//if( this._tracker === undefined )
// this._tracker = this.matcher.tracker;//this.matcher.getInputTracker();
//return this._tracker;
return this.matcher.tracker;
}
}, {
key: "getFullTracker",
value: function getFullTracker() {
var t = this.getInputTracker();
var rest = this.matcher.minChars().map(function (c) {
return (0, _regexUtils.isMeta)(c) ? [c, undefined] : [c, c];
});
//return append(t,rest);
return [].concat(t, rest);
}
}, {
key: "_resetCache",
value: function _resetCache() {
//this._tracker = undefined;
this._lastEditableIndex = undefined;
}
}, {
key: "stateStr",
value: function stateStr() {
return this.matcher.stateStr();
}
}, {
key: "length",
get: function get() {
return this.matcher.length;
}
}]);
return RxMatcher;
}();