diff --git a/dist/browser/filtrex.js b/dist/browser/filtrex.js index b393f72..8b72634 100644 --- a/dist/browser/filtrex.js +++ b/dist/browser/filtrex.js @@ -118,58 +118,58 @@ var filtrex = (function (exports) { case 1: return $$[$0 - 1]; case 2: - this.$ = ["(", "", $$[$0 - 2], " + ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 3: - this.$ = ["(", "", $$[$0 - 2], " - ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 4: - this.$ = ["(", "", $$[$0 - 2], " * ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 5: - this.$ = ["(", "", $$[$0 - 2], " / ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 6: - this.$ = ["(", "", $$[$0 - 2], " % ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 7: - this.$ = ["(", "Math.pow( ", $$[$0 - 2], ", ", $$[$0], " )", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 8: this.$ = ["(", "- ", $$[$0], "", ")"]; break; case 9: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 2], ") && ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 2], " && ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 10: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 2], ") || ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 2], " || ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 11: - this.$ = ["(", "! ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "! ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 12: - this.$ = ["(", "", $$[$0 - 2], " === ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 13: - this.$ = ["(", "", $$[$0 - 2], " !== ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 14: - this.$ = ["(", "RegExp(", $$[$0], ").test(", $$[$0 - 2], ")", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 15: - this.$ = ["(", "", $$[$0 - 2], " < ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 16: - this.$ = ["(", "", $$[$0 - 2], " <= ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 17: - this.$ = ["(", "", $$[$0 - 2], " > ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 18: - this.$ = ["(", "", $$[$0 - 2], " >= ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 19: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 4], ") ? ", $$[$0 - 2], " : ", $$[$0], "", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 4], " ? ", $$[$0 - 2], " : ", $$[$0], "", ")"]; break; case 20: this.$ = ["(", "", $$[$0 - 1], "", ")"]; @@ -1793,63 +1793,130 @@ var filtrex = (function (exports) { } } - // the parser is dynamically generated from generateParser.js at compile time - // Shared utility functions - const std = - { + /** + * Mathematically correct modulo + * @param {number} a + * @param {number} b + * @returns {number} + */ - isfn: function(fns, funcName) { - return hasOwnProperty(fns, funcName) && typeof fns[funcName] === "function"; - }, + function mod(a, b) { + return (a % b + b) % b + } - unknown: function(funcName) { - throw new ReferenceError('Unknown function: ' + funcName + '()'); - }, - coerceArray: function(value) { - if (value === undefined || value === null) { - throw new TypeError(`Expected a list, but got ${value} instead.`) - } - if (Array.isArray(value)) { - return value; - } else { - return [value]; - } - }, + // Type assertions/coertions - coerceNumber: function (value) { - const origValue = value; + function num(value) { + const origValue = value; - if (value === undefined || value === null) - throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) - if (Array.isArray(value) && value.length === 1) - value = value[0]; + if (Array.isArray(value) && value.length === 1) + value = value[0]; - if (typeof value === 'object') - value = toPrimitive(value); + if (typeof value === 'object') + value = toPrimitive(value, 'number'); - if (typeof value === 'number' || typeof value === 'bigint') - return value; + if (typeof value === 'number') + return value; - throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) - }, + throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) + } + + function str(value) { + const origValue = value; + + if (value === undefined || value === null) + throw new TypeError(`Expected a text, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0]; + + if (typeof value === 'object') + value = toPrimitive(value, 'string'); + + if (typeof value === 'string') + return value; + + throw new TypeError(`Expected a text, but got an ${typeof origValue} instead.`) + } + + function numstr(value) { + const origValue = value; + let converted; + + if (typeof value === 'string' || typeof value === 'number') + return value + + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0]; + + if (typeof value === 'object') { + converted = toPrimitive(value, 'number'); + + if (typeof converted === 'number') + return converted; + + converted = toPrimitive(value, 'string'); + + if (typeof converted === 'string') + return converted; + } + + throw new TypeError(`Expected a text or a numeric value, but got an ${typeof origValue} instead.`) + } + + function bool(value) { + if (typeof value === 'boolean') + return value + + if (typeof value === 'object' && value instanceof Boolean) + return value.valueOf(); + + throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) + } + + function arr(value) { + if (value === undefined || value === null) { + throw new TypeError(`Expected a list, but got ${value} instead.`) + } - coerceBoolean: function(value) { - if (typeof value === 'boolean') - return value + if (Array.isArray(value)) { + return value; + } else { + return [value]; + } + } + + // the parser is dynamically generated from generateParser.js at compile time + + // Shared utility functions + const std = + { - if (typeof value === 'object' && value instanceof Boolean) - return value.valueOf(); + isfn: function(fns, funcName) { + return hasOwnProperty(fns, funcName) && typeof fns[funcName] === "function"; + }, - throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) + unknown: function(funcName) { + throw new ReferenceError('Unknown function: ' + funcName + '()'); }, + coerceArray: arr, + coerceNumber: num, + coerceNumberOrString: numstr, + coerceBoolean: bool, + isSubset: function(a, b) { - const A = std.coerceArray(a); - const B = std.coerceArray(b); + const A = arr(a); + const B = arr(b); return A.every( val => B.includes(val) ); }, @@ -1906,10 +1973,11 @@ var filtrex = (function (exports) { if (arguments.length > 2) throw new TypeError('Too many arguments.'); options = typeof options === "object" ? options : {}; - let {extraFunctions, customProp} = options; - for (let key of Object.getOwnPropertyNames(options)) + let {extraFunctions, customProp, operators} = options; + for (const key of Object.keys(options)) { - if (key !== "extraFunctions" && key !== "customProp") throw new TypeError(`Unknown option: ${key}`); + if (!(["extraFunctions", "customProp", "operators"].includes(key))) + throw new TypeError(`Unknown option: ${key}`); } @@ -1931,13 +1999,39 @@ var filtrex = (function (exports) { }; if (extraFunctions) { - for (var name in extraFunctions) { - if (hasOwnProperty(extraFunctions, name)) { - functions[name] = extraFunctions[name]; - } + for (const name of Object.keys(extraFunctions)) { + functions[name] = extraFunctions[name]; } } + let defaultOperators = { + '+': (a, b) => numstr(a) + numstr(b), + '-': (a, b) => b === undefined ? -num(a) : num(a) - num(b), + '*': (a, b) => num(a) * num(b), + '/': (a, b) => num(a) / num(b), + + '%': (a, b) => mod(num(a), num(b)), + '^': (a, b) => Math.pow(num(a), num(b)), + + '==': (a, b) => a === b, + '!=': (a, b) => a !== b, + + '<': (a, b) => num(a) < num(b), + '<=': (a, b) => num(a) <= num(b), + '>=': (a, b) => num(a) >= num(b), + '>': (a, b) => num(a) > num(b), + + '~=': (a, b) => RegExp(str(b)).test(str(a)) + }; + + if (operators) { + for (const name of Object.keys(operators)) { + defaultOperators[name] = operators[name]; + } + } + + operators = defaultOperators; + // Compile the expression @@ -1984,11 +2078,11 @@ var filtrex = (function (exports) { // Patch together and return - let func = new Function('fns', 'std', 'prop', 'data', js.join('')); + let func = new Function('fns', 'ops', 'std', 'prop', 'data', js.join('')); return function(data) { try { - return func(functions, std, prop, data); + return func(functions, operators, std, prop, data); } catch (e) { diff --git a/dist/browser/filtrex.min.js b/dist/browser/filtrex.min.js index 2c0f030..0dfce15 100644 --- a/dist/browser/filtrex.min.js +++ b/dist/browser/filtrex.min.js @@ -1 +1 @@ -var filtrex=function(t){"use strict";var e=function(){var t={trace:function(){},yy:{},symbols_:{error:2,expressions:3,e:4,EOF:5,"+":6,"-":7,"*":8,"/":9,"%":10,"^":11,and:12,or:13,not:14,"==":15,"!=":16,"~=":17,"<":18,"<=":19,">":20,">=":21,if:22,then:23,else:24,"(":25,")":26,array:27,",":28,NUMBER:29,STRING:30,SYMBOL:31,of:32,argsList:33,in:34,inSet:35,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",6:"+",7:"-",8:"*",9:"/",10:"%",11:"^",12:"and",13:"or",14:"not",15:"==",16:"!=",17:"~=",18:"<",19:"<=",20:">",21:">=",22:"if",23:"then",24:"else",25:"(",26:")",28:",",29:"NUMBER",30:"STRING",31:"SYMBOL",32:"of",34:"in"},productions_:[0,[3,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,2],[4,3],[4,3],[4,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,6],[4,3],[4,5],[4,1],[4,1],[4,1],[4,3],[4,3],[4,4],[4,3],[4,4],[33,1],[33,3],[35,1],[35,3],[27,1],[27,3]],performAction:function(t,e,n,r,i,s,o){var h=s.length-1;switch(i){case 1:return s[h-1];case 2:this.$=["(","",s[h-2]," + ",s[h],"",")"];break;case 3:this.$=["(","",s[h-2]," - ",s[h],"",")"];break;case 4:this.$=["(","",s[h-2]," * ",s[h],"",")"];break;case 5:this.$=["(","",s[h-2]," / ",s[h],"",")"];break;case 6:this.$=["(","",s[h-2]," % ",s[h],"",")"];break;case 7:this.$=["(","Math.pow( ",s[h-2],", ",s[h]," )",")"];break;case 8:this.$=["(","- ",s[h],"",")"];break;case 9:this.$=["(","","std.coerceBoolean","(",s[h-2],") && ","std.coerceBoolean","(",s[h],")",")"];break;case 10:this.$=["(","","std.coerceBoolean","(",s[h-2],") || ","std.coerceBoolean","(",s[h],")",")"];break;case 11:this.$=["(","! ","std.coerceBoolean","(",s[h],")",")"];break;case 12:this.$=["(","",s[h-2]," === ",s[h],"",")"];break;case 13:this.$=["(","",s[h-2]," !== ",s[h],"",")"];break;case 14:this.$=["(","RegExp(",s[h],").test(",s[h-2],")",")"];break;case 15:this.$=["(","",s[h-2]," < ",s[h],"",")"];break;case 16:this.$=["(","",s[h-2]," <= ",s[h],"",")"];break;case 17:this.$=["(","",s[h-2]," > ",s[h],"",")"];break;case 18:this.$=["(","",s[h-2]," >= ",s[h],"",")"];break;case 19:this.$=["(","","std.coerceBoolean","(",s[h-4],") ? ",s[h-2]," : ",s[h],"",")"];break;case 20:this.$=["(","",s[h-1],"",")"];break;case 21:this.$=["(","[ ",s[h-3],", ",s[h-1]," ]",")"];break;case 22:case 23:this.$=["(","",s[h],"",")"];break;case 24:this.$=["(","prop(",s[h],", data)",")"];break;case 25:this.$=["(","prop(",s[h-2],", ",s[h],")",")"];break;case 26:this.$=["(","std.isfn(fns, ",s[h-2],") ? fns[",s[h-2],"]() : std.unknown(",s[h-2],")",")"];break;case 27:this.$=["(","std.isfn(fns, ",s[h-3],") ? fns[",s[h-3],"](",s[h-1],") : std.unknown(",s[h-3],")",")"];break;case 28:this.$=["(","std.isSubset(",s[h-2],", ",s[h],")",")"];break;case 29:this.$=["(","!std.isSubset(",s[h-3],", ",s[h],")",")"];break;case 30:this.$=["",s[h],""];break;case 31:this.$=["",s[h-2],", ",s[h],""];break;case 32:this.$=["o === ",s[h],""];break;case 33:this.$=["",s[h-2]," || o === ",s[h],""];break;case 34:this.$=["",s[h],""];break;case 35:this.$=["",s[h-2],", ",s[h],""]}},table:[{3:1,4:2,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{1:[3]},{5:[1,10],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],34:[1,26]},{4:28,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:29,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:30,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:31,7:[1,3],14:[1,4],22:[1,5],25:[1,6],27:32,29:[1,7],30:[1,8],31:[1,9]},{5:[2,22],6:[2,22],7:[2,22],8:[2,22],9:[2,22],10:[2,22],11:[2,22],12:[2,22],13:[2,22],14:[2,22],15:[2,22],16:[2,22],17:[2,22],18:[2,22],19:[2,22],20:[2,22],21:[2,22],23:[2,22],24:[2,22],26:[2,22],28:[2,22],34:[2,22]},{5:[2,23],6:[2,23],7:[2,23],8:[2,23],9:[2,23],10:[2,23],11:[2,23],12:[2,23],13:[2,23],14:[2,23],15:[2,23],16:[2,23],17:[2,23],18:[2,23],19:[2,23],20:[2,23],21:[2,23],23:[2,23],24:[2,23],26:[2,23],28:[2,23],34:[2,23]},{5:[2,24],6:[2,24],7:[2,24],8:[2,24],9:[2,24],10:[2,24],11:[2,24],12:[2,24],13:[2,24],14:[2,24],15:[2,24],16:[2,24],17:[2,24],18:[2,24],19:[2,24],20:[2,24],21:[2,24],23:[2,24],24:[2,24],25:[1,34],26:[2,24],28:[2,24],32:[1,33],34:[2,24]},{1:[2,1]},{4:35,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:36,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:37,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:38,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:39,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:40,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:41,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:42,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:43,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:44,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:45,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:46,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:47,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:48,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:49,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:50,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{34:[1,51]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[2,8],17:[2,8],18:[2,8],19:[2,8],20:[2,8],21:[2,8],23:[2,8],24:[2,8],26:[2,8],28:[2,8],34:[2,8]},{5:[2,11],6:[2,11],7:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],17:[2,11],18:[2,11],19:[2,11],20:[2,11],21:[2,11],23:[2,11],24:[2,11],26:[2,11],28:[2,11],34:[2,11]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[1,52],34:[1,26]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[1,53],28:[2,34],34:[1,26]},{28:[1,54]},{4:55,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:58,7:[1,3],14:[1,4],22:[1,5],25:[1,6],26:[1,56],29:[1,7],30:[1,8],31:[1,9],33:57},{5:[2,2],6:[2,2],7:[2,2],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,2],13:[2,2],14:[1,27],15:[2,2],16:[2,2],17:[2,2],18:[2,2],19:[2,2],20:[2,2],21:[2,2],23:[2,2],24:[2,2],26:[2,2],28:[2,2],34:[2,2]},{5:[2,3],6:[2,3],7:[2,3],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,3],13:[2,3],14:[1,27],15:[2,3],16:[2,3],17:[2,3],18:[2,3],19:[2,3],20:[2,3],21:[2,3],23:[2,3],24:[2,3],26:[2,3],28:[2,3],34:[2,3]},{5:[2,4],6:[2,4],7:[2,4],8:[2,4],9:[2,4],10:[2,4],11:[1,16],12:[2,4],13:[2,4],14:[1,27],15:[2,4],16:[2,4],17:[2,4],18:[2,4],19:[2,4],20:[2,4],21:[2,4],23:[2,4],24:[2,4],26:[2,4],28:[2,4],34:[2,4]},{5:[2,5],6:[2,5],7:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[1,16],12:[2,5],13:[2,5],14:[1,27],15:[2,5],16:[2,5],17:[2,5],18:[2,5],19:[2,5],20:[2,5],21:[2,5],23:[2,5],24:[2,5],26:[2,5],28:[2,5],34:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[1,16],12:[2,6],13:[2,6],14:[1,27],15:[2,6],16:[2,6],17:[2,6],18:[2,6],19:[2,6],20:[2,6],21:[2,6],23:[2,6],24:[2,6],26:[2,6],28:[2,6],34:[2,6]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[1,27],15:[2,7],16:[2,7],17:[2,7],18:[2,7],19:[2,7],20:[2,7],21:[2,7],23:[2,7],24:[2,7],26:[2,7],28:[2,7],34:[2,7]},{5:[2,9],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,9],13:[2,9],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,9],24:[2,9],26:[2,9],28:[2,9],34:[1,26]},{5:[2,10],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[2,10],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,10],24:[2,10],26:[2,10],28:[2,10],34:[1,26]},{5:[2,12],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,12],13:[2,12],14:[1,27],15:[2,12],16:[2,12],17:[2,12],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,12],24:[2,12],26:[2,12],28:[2,12],34:[2,12]},{5:[2,13],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,13],13:[2,13],14:[1,27],15:[2,13],16:[2,13],17:[2,13],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,13],24:[2,13],26:[2,13],28:[2,13],34:[2,13]},{5:[2,14],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,14],13:[2,14],14:[1,27],15:[2,14],16:[2,14],17:[2,14],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,14],24:[2,14],26:[2,14],28:[2,14],34:[2,14]},{5:[2,15],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,15],13:[2,15],14:[1,27],15:[2,15],16:[2,15],17:[2,15],18:[2,15],19:[2,15],20:[2,15],21:[2,15],23:[2,15],24:[2,15],26:[2,15],28:[2,15],34:[2,15]},{5:[2,16],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,16],13:[2,16],14:[1,27],15:[2,16],16:[2,16],17:[2,16],18:[2,16],19:[2,16],20:[2,16],21:[2,16],23:[2,16],24:[2,16],26:[2,16],28:[2,16],34:[2,16]},{5:[2,17],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,17],13:[2,17],14:[1,27],15:[2,17],16:[2,17],17:[2,17],18:[2,17],19:[2,17],20:[2,17],21:[2,17],23:[2,17],24:[2,17],26:[2,17],28:[2,17],34:[2,17]},{5:[2,18],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,18],13:[2,18],14:[1,27],15:[2,18],16:[2,18],17:[2,18],18:[2,18],19:[2,18],20:[2,18],21:[2,18],23:[2,18],24:[2,18],26:[2,18],28:[2,18],34:[2,18]},{5:[2,28],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,28],13:[2,28],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,28],24:[2,28],26:[2,28],28:[2,28],34:[2,28]},{4:59,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:60,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{5:[2,20],6:[2,20],7:[2,20],8:[2,20],9:[2,20],10:[2,20],11:[2,20],12:[2,20],13:[2,20],14:[2,20],15:[2,20],16:[2,20],17:[2,20],18:[2,20],19:[2,20],20:[2,20],21:[2,20],23:[2,20],24:[2,20],26:[2,20],28:[2,20],34:[2,20]},{4:61,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{5:[2,25],6:[2,25],7:[2,25],8:[2,25],9:[2,25],10:[2,25],11:[2,25],12:[2,25],13:[2,25],14:[2,25],15:[2,25],16:[2,25],17:[2,25],18:[2,25],19:[2,25],20:[2,25],21:[2,25],23:[2,25],24:[2,25],26:[2,25],28:[2,25],34:[2,25]},{5:[2,26],6:[2,26],7:[2,26],8:[2,26],9:[2,26],10:[2,26],11:[2,26],12:[2,26],13:[2,26],14:[2,26],15:[2,26],16:[2,26],17:[2,26],18:[2,26],19:[2,26],20:[2,26],21:[2,26],23:[2,26],24:[2,26],26:[2,26],28:[2,26],34:[2,26]},{26:[1,62],28:[1,63]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[2,30],28:[2,30],34:[1,26]},{5:[2,29],6:[2,29],7:[2,29],8:[2,29],9:[2,29],10:[2,29],11:[2,29],12:[2,29],13:[2,29],14:[2,29],15:[2,29],16:[2,29],17:[2,29],18:[2,29],19:[2,29],20:[2,29],21:[2,29],23:[2,29],24:[2,29],26:[2,29],28:[2,29],34:[2,29]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],24:[1,64],34:[1,26]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[1,65],28:[2,35],34:[1,26]},{5:[2,27],6:[2,27],7:[2,27],8:[2,27],9:[2,27],10:[2,27],11:[2,27],12:[2,27],13:[2,27],14:[2,27],15:[2,27],16:[2,27],17:[2,27],18:[2,27],19:[2,27],20:[2,27],21:[2,27],23:[2,27],24:[2,27],26:[2,27],28:[2,27],34:[2,27]},{4:66,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:67,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{5:[2,21],6:[2,21],7:[2,21],8:[2,21],9:[2,21],10:[2,21],11:[2,21],12:[2,21],13:[2,21],14:[2,21],15:[2,21],16:[2,21],17:[2,21],18:[2,21],19:[2,21],20:[2,21],21:[2,21],23:[2,21],24:[2,21],26:[2,21],28:[2,21],34:[2,21]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[2,31],28:[2,31],34:[1,26]},{5:[2,19],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,19],24:[2,19],26:[2,19],28:[2,19],34:[1,26]}],defaultActions:{10:[2,1]},parseError:function(t,e){throw new Error(t)},parse:function(t){var e=this,n=[0],r=[null],i=[],s=this.table,o="",h=0,a=0,l=0;this.lexer.setInput(t),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,void 0===this.lexer.yylloc&&(this.lexer.yylloc={});var c=this.lexer.yylloc;i.push(c);var u=this.lexer.options&&this.lexer.options.ranges;function y(){var t;return"number"!=typeof(t=e.lexer.lex()||1)&&(t=e.symbols_[t]||t),t}"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var f,p,g,d,m,b,x,_,w,k,$={};;){if(g=n[n.length-1],this.defaultActions[g]?d=this.defaultActions[g]:(null==f&&(f=y()),d=s[g]&&s[g][f]),void 0===d||!d.length||!d[0]){var E="";if(!l){for(b in w=[],s[g])this.terminals_[b]&&b>2&&w.push("'"+this.terminals_[b]+"'");E=this.lexer.showPosition?"Parse error on line "+(h+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+w.join(", ")+", got '"+(this.terminals_[f]||f)+"'":"Parse error on line "+(h+1)+": Unexpected "+(1==f?"end of input":"'"+(this.terminals_[f]||f)+"'"),this.parseError(E,{text:this.lexer.match,token:this.terminals_[f]||f,line:this.lexer.yylineno,loc:c,expected:w})}if(3==l){if(1==f)throw new Error(E||"Parsing halted.");a=this.lexer.yyleng,o=this.lexer.yytext,h=this.lexer.yylineno,c=this.lexer.yylloc,f=y()}for(;!(2..toString()in s[g]);){if(0===g)throw new Error(E||"Parsing halted.");k=1,n.length=n.length-2*k,r.length=r.length-k,i.length=i.length-k,g=n[n.length-1]}p=2==f?null:f,f=2,d=s[g=n[n.length-1]]&&s[g][2],l=3}if(d[0]instanceof Array&&d.length>1)throw new Error("Parse Error: multiple actions possible at state: "+g+", token: "+f);switch(d[0]){case 1:n.push(f),r.push(this.lexer.yytext),i.push(this.lexer.yylloc),n.push(d[1]),f=null,p?(f=p,p=null):(a=this.lexer.yyleng,o=this.lexer.yytext,h=this.lexer.yylineno,c=this.lexer.yylloc,l>0&&l--);break;case 2:if(x=this.productions_[d[1]][1],$.$=r[r.length-x],$._$={first_line:i[i.length-(x||1)].first_line,last_line:i[i.length-1].last_line,first_column:i[i.length-(x||1)].first_column,last_column:i[i.length-1].last_column},u&&($._$.range=[i[i.length-(x||1)].range[0],i[i.length-1].range[1]]),void 0!==(m=this.performAction.call($,o,a,h,this.yy,d[1],r,i)))return m;x&&(n=n.slice(0,-1*x*2),r=r.slice(0,-1*x),i=i.slice(0,-1*x)),n.push(this.productions_[d[1]][0]),r.push($.$),i.push($._$),_=s[n[n.length-2]][n[n.length-1]],n.push(_);break;case 3:return!0}}return!0}},e=function(){var t={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t){return this._input=t,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e-1),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this},more:function(){return this._more=!0,this},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},next:function(){if(this.done)return this.EOF;var t,e,n,r,i;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var s=this._currentRules(),o=0;oe[0].length)||(e=n,r=o,this.options.flex));o++);return e?((i=e[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=i.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:i?i[i.length-1].length-i[i.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],t=this.performAction.call(this,this.yy,this,s[r],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),t||void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return void 0!==t?t:this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(t){this.begin(t)},options:{},performAction:function(t,e,n,r){switch(n){case 0:return"*";case 1:return"/";case 2:return"-";case 3:return"+";case 4:return"^";case 5:return"%";case 6:return"(";case 7:return")";case 8:return",";case 9:return"==";case 10:return"!=";case 11:return"~=";case 12:return">=";case 13:return"<=";case 14:return"<";case 15:return">";case 16:return"and";case 17:return"or";case 18:return"not";case 19:return"in";case 20:return"of";case 21:return"if";case 22:return"then";case 23:return"else";case 24:break;case 25:return"NUMBER";case 26:return e.yytext=JSON.stringify(e.yytext),"SYMBOL";case 27:return e.yytext=t.buildString("'",e.yytext),"SYMBOL";case 28:return e.yytext=t.buildString('"',e.yytext),"STRING";case 29:return"EOF"}},rules:[/^(?:\*)/,/^(?:\/)/,/^(?:-)/,/^(?:\+)/,/^(?:\^)/,/^(?:\%)/,/^(?:\()/,/^(?:\))/,/^(?:\,)/,/^(?:==)/,/^(?:\!=)/,/^(?:\~=)/,/^(?:>=)/,/^(?:<=)/,/^(?:<)/,/^(?:>)/,/^(?:and[^\w])/,/^(?:or[^\w])/,/^(?:not[^\w])/,/^(?:in[^\w])/,/^(?:of[^\w])/,/^(?:if[^\w])/,/^(?:then[^\w])/,/^(?:else[^\w])/,/^(?:\s+)/,/^(?:[0-9]+(?:\.[0-9]+)?\b)/,/^(?:[a-zA-Z$_][\.a-zA-Z0-9$_]*)/,/^(?:'(?:\\'|\\\\|[^'\\])*')/,/^(?:"(?:\\"|\\\\|[^"\\])*")/,/^(?:$)/],conditions:{INITIAL:{rules:[0,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],inclusive:!0}}};return t}();function n(){this.yy={}}return t.lexer=e,n.prototype=t,t.Parser=n,new n}();const n=e;function r(t,e){return("object"==typeof t||"function"==typeof t)&&Object.prototype.hasOwnProperty.call(t,e)}e.Parser;const i="function"!=typeof Symbol||"symbol"!=typeof Symbol.toPrimitive?()=>{}:t=>{if("function"==typeof t[Symbol.toPrimitive])return t[Symbol.toPrimitive]};const s={isfn:function(t,e){return r(t,e)&&"function"==typeof t[e]},unknown:function(t){throw new ReferenceError("Unknown function: "+t+"()")},coerceArray:function(t){if(null==t)throw new TypeError(`Expected a list, but got ${t} instead.`);return Array.isArray(t)?t:[t]},coerceNumber:function(t){const e=t;if(null==t)throw new TypeError(`Expected a numeric value, but got ${t} instead.`);if(Array.isArray(t)&&1===t.length&&(t=t[0]),"object"==typeof t&&(t=function(t,e){if("object"==typeof t&&"function"==typeof t)try{let n=i(t)(e);if(typeof n===e)return n;if(n=t.valueOf(),typeof n===e)return n}finally{return}}(t)),"number"==typeof t||"bigint"==typeof t)return t;throw new TypeError(`Expected a numeric value, but got an ${typeof e} instead.`)},coerceBoolean:function(t){if("boolean"==typeof t)return t;if("object"==typeof t&&t instanceof Boolean)return t.valueOf();throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof t} instead.`)},isSubset:function(t,e){const n=s.coerceArray(t),r=s.coerceArray(e);return n.every(t=>r.includes(t))},buildString:function(t,e){t=String(t)[0];let n="";if((e=String(e))[0]!==t||e[e.length-1]!==t)throw new Error("Unexpected internal error: String literal doesn't begin/end with the right quotation mark.");for(let r=1;r=e.length-1)throw new Error("Unexpected internal error: Unescaped backslash at the end of string literal.");if("\\"===e[r])n+="\\";else{if(e[r]!==t)throw new Error("Unexpected internal error: Invalid escaped character in string literal: "+e[r]);n+=t}}else{if(e[r]===t)throw new Error("Unexpected internal error: String literal contains unescaped quotation mark.");n+=e[r]}return JSON.stringify(n)}};return n.yy=Object.create(s),t.compileExpression=function(t,e){if(arguments.length>2)throw new TypeError("Too many arguments.");e="object"==typeof e?e:{};let{extraFunctions:i,customProp:o}=e;for(let t of Object.getOwnPropertyNames(e))if("extraFunctions"!==t&&"customProp"!==t)throw new TypeError("Unknown option: "+t);let h={abs:Math.abs,ceil:Math.ceil,floor:Math.floor,log:Math.log,max:Math.max,min:Math.min,random:Math.random,round:Math.round,sqrt:Math.sqrt,exists:t=>null!=t,empty:t=>null==t||""===t||Array.isArray(t)&&0===t.length};if(i)for(var a in i)r(i,a)&&(h[a]=i[a]);let l=n.parse(t),c=[];function u(t){Array.isArray(t)?t.forEach(u):c.push(t)}function y(t,e){if(r(e||{},t))return e[t];throw new ReferenceError(`Property “${t}” does not exist.`)}function f(t){return function(e){if(r(t||{},e))return t[e];throw new ReferenceError(`Property “${e}” does not exist.`)}}c.push("return "),l.forEach(u),c.push(";"),"function"==typeof o&&(y=(t,e)=>o(t,f(e),e));let p=new Function("fns","std","prop","data",c.join(""));return function(t){try{return p(h,s,y,t)}catch(t){return t}}},Object.defineProperty(t,"__esModule",{value:!0}),t}({}); \ No newline at end of file +var filtrex=function(t){"use strict";var e=function(){var t={trace:function(){},yy:{},symbols_:{error:2,expressions:3,e:4,EOF:5,"+":6,"-":7,"*":8,"/":9,"%":10,"^":11,and:12,or:13,not:14,"==":15,"!=":16,"~=":17,"<":18,"<=":19,">":20,">=":21,if:22,then:23,else:24,"(":25,")":26,array:27,",":28,NUMBER:29,STRING:30,SYMBOL:31,of:32,argsList:33,in:34,inSet:35,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",6:"+",7:"-",8:"*",9:"/",10:"%",11:"^",12:"and",13:"or",14:"not",15:"==",16:"!=",17:"~=",18:"<",19:"<=",20:">",21:">=",22:"if",23:"then",24:"else",25:"(",26:")",28:",",29:"NUMBER",30:"STRING",31:"SYMBOL",32:"of",34:"in"},productions_:[0,[3,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,2],[4,3],[4,3],[4,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,6],[4,3],[4,5],[4,1],[4,1],[4,1],[4,3],[4,3],[4,4],[4,3],[4,4],[33,1],[33,3],[35,1],[35,3],[27,1],[27,3]],performAction:function(t,e,n,r,i,s,o){var h=s.length-1;switch(i){case 1:return s[h-1];case 2:case 3:case 4:case 5:case 6:case 7:this.$=["(","ops['",s[h-1],"'](",s[h-2],", ",s[h],")",")"];break;case 8:this.$=["(","- ",s[h],"",")"];break;case 9:this.$=["(","","std.coerceBoolean","",s[h-2]," && ","std.coerceBoolean","",s[h],"",")"];break;case 10:this.$=["(","","std.coerceBoolean","",s[h-2]," || ","std.coerceBoolean","",s[h],"",")"];break;case 11:this.$=["(","! ","std.coerceBoolean","",s[h],"",")"];break;case 12:case 13:case 14:case 15:case 16:case 17:case 18:this.$=["(","ops['",s[h-1],"'](",s[h-2],", ",s[h],")",")"];break;case 19:this.$=["(","","std.coerceBoolean","",s[h-4]," ? ",s[h-2]," : ",s[h],"",")"];break;case 20:this.$=["(","",s[h-1],"",")"];break;case 21:this.$=["(","[ ",s[h-3],", ",s[h-1]," ]",")"];break;case 22:case 23:this.$=["(","",s[h],"",")"];break;case 24:this.$=["(","prop(",s[h],", data)",")"];break;case 25:this.$=["(","prop(",s[h-2],", ",s[h],")",")"];break;case 26:this.$=["(","std.isfn(fns, ",s[h-2],") ? fns[",s[h-2],"]() : std.unknown(",s[h-2],")",")"];break;case 27:this.$=["(","std.isfn(fns, ",s[h-3],") ? fns[",s[h-3],"](",s[h-1],") : std.unknown(",s[h-3],")",")"];break;case 28:this.$=["(","std.isSubset(",s[h-2],", ",s[h],")",")"];break;case 29:this.$=["(","!std.isSubset(",s[h-3],", ",s[h],")",")"];break;case 30:this.$=["",s[h],""];break;case 31:this.$=["",s[h-2],", ",s[h],""];break;case 32:this.$=["o === ",s[h],""];break;case 33:this.$=["",s[h-2]," || o === ",s[h],""];break;case 34:this.$=["",s[h],""];break;case 35:this.$=["",s[h-2],", ",s[h],""]}},table:[{3:1,4:2,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{1:[3]},{5:[1,10],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],34:[1,26]},{4:28,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:29,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:30,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:31,7:[1,3],14:[1,4],22:[1,5],25:[1,6],27:32,29:[1,7],30:[1,8],31:[1,9]},{5:[2,22],6:[2,22],7:[2,22],8:[2,22],9:[2,22],10:[2,22],11:[2,22],12:[2,22],13:[2,22],14:[2,22],15:[2,22],16:[2,22],17:[2,22],18:[2,22],19:[2,22],20:[2,22],21:[2,22],23:[2,22],24:[2,22],26:[2,22],28:[2,22],34:[2,22]},{5:[2,23],6:[2,23],7:[2,23],8:[2,23],9:[2,23],10:[2,23],11:[2,23],12:[2,23],13:[2,23],14:[2,23],15:[2,23],16:[2,23],17:[2,23],18:[2,23],19:[2,23],20:[2,23],21:[2,23],23:[2,23],24:[2,23],26:[2,23],28:[2,23],34:[2,23]},{5:[2,24],6:[2,24],7:[2,24],8:[2,24],9:[2,24],10:[2,24],11:[2,24],12:[2,24],13:[2,24],14:[2,24],15:[2,24],16:[2,24],17:[2,24],18:[2,24],19:[2,24],20:[2,24],21:[2,24],23:[2,24],24:[2,24],25:[1,34],26:[2,24],28:[2,24],32:[1,33],34:[2,24]},{1:[2,1]},{4:35,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:36,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:37,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:38,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:39,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:40,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:41,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:42,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:43,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:44,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:45,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:46,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:47,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:48,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:49,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:50,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{34:[1,51]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[2,8],17:[2,8],18:[2,8],19:[2,8],20:[2,8],21:[2,8],23:[2,8],24:[2,8],26:[2,8],28:[2,8],34:[2,8]},{5:[2,11],6:[2,11],7:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],17:[2,11],18:[2,11],19:[2,11],20:[2,11],21:[2,11],23:[2,11],24:[2,11],26:[2,11],28:[2,11],34:[2,11]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[1,52],34:[1,26]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[1,53],28:[2,34],34:[1,26]},{28:[1,54]},{4:55,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:58,7:[1,3],14:[1,4],22:[1,5],25:[1,6],26:[1,56],29:[1,7],30:[1,8],31:[1,9],33:57},{5:[2,2],6:[2,2],7:[2,2],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,2],13:[2,2],14:[1,27],15:[2,2],16:[2,2],17:[2,2],18:[2,2],19:[2,2],20:[2,2],21:[2,2],23:[2,2],24:[2,2],26:[2,2],28:[2,2],34:[2,2]},{5:[2,3],6:[2,3],7:[2,3],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,3],13:[2,3],14:[1,27],15:[2,3],16:[2,3],17:[2,3],18:[2,3],19:[2,3],20:[2,3],21:[2,3],23:[2,3],24:[2,3],26:[2,3],28:[2,3],34:[2,3]},{5:[2,4],6:[2,4],7:[2,4],8:[2,4],9:[2,4],10:[2,4],11:[1,16],12:[2,4],13:[2,4],14:[1,27],15:[2,4],16:[2,4],17:[2,4],18:[2,4],19:[2,4],20:[2,4],21:[2,4],23:[2,4],24:[2,4],26:[2,4],28:[2,4],34:[2,4]},{5:[2,5],6:[2,5],7:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[1,16],12:[2,5],13:[2,5],14:[1,27],15:[2,5],16:[2,5],17:[2,5],18:[2,5],19:[2,5],20:[2,5],21:[2,5],23:[2,5],24:[2,5],26:[2,5],28:[2,5],34:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[1,16],12:[2,6],13:[2,6],14:[1,27],15:[2,6],16:[2,6],17:[2,6],18:[2,6],19:[2,6],20:[2,6],21:[2,6],23:[2,6],24:[2,6],26:[2,6],28:[2,6],34:[2,6]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[1,27],15:[2,7],16:[2,7],17:[2,7],18:[2,7],19:[2,7],20:[2,7],21:[2,7],23:[2,7],24:[2,7],26:[2,7],28:[2,7],34:[2,7]},{5:[2,9],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,9],13:[2,9],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,9],24:[2,9],26:[2,9],28:[2,9],34:[1,26]},{5:[2,10],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[2,10],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,10],24:[2,10],26:[2,10],28:[2,10],34:[1,26]},{5:[2,12],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,12],13:[2,12],14:[1,27],15:[2,12],16:[2,12],17:[2,12],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,12],24:[2,12],26:[2,12],28:[2,12],34:[2,12]},{5:[2,13],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,13],13:[2,13],14:[1,27],15:[2,13],16:[2,13],17:[2,13],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,13],24:[2,13],26:[2,13],28:[2,13],34:[2,13]},{5:[2,14],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,14],13:[2,14],14:[1,27],15:[2,14],16:[2,14],17:[2,14],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,14],24:[2,14],26:[2,14],28:[2,14],34:[2,14]},{5:[2,15],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,15],13:[2,15],14:[1,27],15:[2,15],16:[2,15],17:[2,15],18:[2,15],19:[2,15],20:[2,15],21:[2,15],23:[2,15],24:[2,15],26:[2,15],28:[2,15],34:[2,15]},{5:[2,16],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,16],13:[2,16],14:[1,27],15:[2,16],16:[2,16],17:[2,16],18:[2,16],19:[2,16],20:[2,16],21:[2,16],23:[2,16],24:[2,16],26:[2,16],28:[2,16],34:[2,16]},{5:[2,17],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,17],13:[2,17],14:[1,27],15:[2,17],16:[2,17],17:[2,17],18:[2,17],19:[2,17],20:[2,17],21:[2,17],23:[2,17],24:[2,17],26:[2,17],28:[2,17],34:[2,17]},{5:[2,18],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,18],13:[2,18],14:[1,27],15:[2,18],16:[2,18],17:[2,18],18:[2,18],19:[2,18],20:[2,18],21:[2,18],23:[2,18],24:[2,18],26:[2,18],28:[2,18],34:[2,18]},{5:[2,28],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[2,28],13:[2,28],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,28],24:[2,28],26:[2,28],28:[2,28],34:[2,28]},{4:59,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:60,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{5:[2,20],6:[2,20],7:[2,20],8:[2,20],9:[2,20],10:[2,20],11:[2,20],12:[2,20],13:[2,20],14:[2,20],15:[2,20],16:[2,20],17:[2,20],18:[2,20],19:[2,20],20:[2,20],21:[2,20],23:[2,20],24:[2,20],26:[2,20],28:[2,20],34:[2,20]},{4:61,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{5:[2,25],6:[2,25],7:[2,25],8:[2,25],9:[2,25],10:[2,25],11:[2,25],12:[2,25],13:[2,25],14:[2,25],15:[2,25],16:[2,25],17:[2,25],18:[2,25],19:[2,25],20:[2,25],21:[2,25],23:[2,25],24:[2,25],26:[2,25],28:[2,25],34:[2,25]},{5:[2,26],6:[2,26],7:[2,26],8:[2,26],9:[2,26],10:[2,26],11:[2,26],12:[2,26],13:[2,26],14:[2,26],15:[2,26],16:[2,26],17:[2,26],18:[2,26],19:[2,26],20:[2,26],21:[2,26],23:[2,26],24:[2,26],26:[2,26],28:[2,26],34:[2,26]},{26:[1,62],28:[1,63]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[2,30],28:[2,30],34:[1,26]},{5:[2,29],6:[2,29],7:[2,29],8:[2,29],9:[2,29],10:[2,29],11:[2,29],12:[2,29],13:[2,29],14:[2,29],15:[2,29],16:[2,29],17:[2,29],18:[2,29],19:[2,29],20:[2,29],21:[2,29],23:[2,29],24:[2,29],26:[2,29],28:[2,29],34:[2,29]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],24:[1,64],34:[1,26]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[1,65],28:[2,35],34:[1,26]},{5:[2,27],6:[2,27],7:[2,27],8:[2,27],9:[2,27],10:[2,27],11:[2,27],12:[2,27],13:[2,27],14:[2,27],15:[2,27],16:[2,27],17:[2,27],18:[2,27],19:[2,27],20:[2,27],21:[2,27],23:[2,27],24:[2,27],26:[2,27],28:[2,27],34:[2,27]},{4:66,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{4:67,7:[1,3],14:[1,4],22:[1,5],25:[1,6],29:[1,7],30:[1,8],31:[1,9]},{5:[2,21],6:[2,21],7:[2,21],8:[2,21],9:[2,21],10:[2,21],11:[2,21],12:[2,21],13:[2,21],14:[2,21],15:[2,21],16:[2,21],17:[2,21],18:[2,21],19:[2,21],20:[2,21],21:[2,21],23:[2,21],24:[2,21],26:[2,21],28:[2,21],34:[2,21]},{6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],26:[2,31],28:[2,31],34:[1,26]},{5:[2,19],6:[1,11],7:[1,12],8:[1,13],9:[1,14],10:[1,15],11:[1,16],12:[1,17],13:[1,18],14:[1,27],15:[1,19],16:[1,20],17:[1,21],18:[1,22],19:[1,23],20:[1,24],21:[1,25],23:[2,19],24:[2,19],26:[2,19],28:[2,19],34:[1,26]}],defaultActions:{10:[2,1]},parseError:function(t,e){throw new Error(t)},parse:function(t){var e=this,n=[0],r=[null],i=[],s=this.table,o="",h=0,a=0,l=0;this.lexer.setInput(t),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,void 0===this.lexer.yylloc&&(this.lexer.yylloc={});var c=this.lexer.yylloc;i.push(c);var u=this.lexer.options&&this.lexer.options.ranges;function y(){var t;return"number"!=typeof(t=e.lexer.lex()||1)&&(t=e.symbols_[t]||t),t}"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var f,p,g,d,m,b,x,w,_,E,k={};;){if(g=n[n.length-1],this.defaultActions[g]?d=this.defaultActions[g]:(null==f&&(f=y()),d=s[g]&&s[g][f]),void 0===d||!d.length||!d[0]){var $="";if(!l){for(b in _=[],s[g])this.terminals_[b]&&b>2&&_.push("'"+this.terminals_[b]+"'");$=this.lexer.showPosition?"Parse error on line "+(h+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+_.join(", ")+", got '"+(this.terminals_[f]||f)+"'":"Parse error on line "+(h+1)+": Unexpected "+(1==f?"end of input":"'"+(this.terminals_[f]||f)+"'"),this.parseError($,{text:this.lexer.match,token:this.terminals_[f]||f,line:this.lexer.yylineno,loc:c,expected:_})}if(3==l){if(1==f)throw new Error($||"Parsing halted.");a=this.lexer.yyleng,o=this.lexer.yytext,h=this.lexer.yylineno,c=this.lexer.yylloc,f=y()}for(;!(2..toString()in s[g]);){if(0===g)throw new Error($||"Parsing halted.");E=1,n.length=n.length-2*E,r.length=r.length-E,i.length=i.length-E,g=n[n.length-1]}p=2==f?null:f,f=2,d=s[g=n[n.length-1]]&&s[g][2],l=3}if(d[0]instanceof Array&&d.length>1)throw new Error("Parse Error: multiple actions possible at state: "+g+", token: "+f);switch(d[0]){case 1:n.push(f),r.push(this.lexer.yytext),i.push(this.lexer.yylloc),n.push(d[1]),f=null,p?(f=p,p=null):(a=this.lexer.yyleng,o=this.lexer.yytext,h=this.lexer.yylineno,c=this.lexer.yylloc,l>0&&l--);break;case 2:if(x=this.productions_[d[1]][1],k.$=r[r.length-x],k._$={first_line:i[i.length-(x||1)].first_line,last_line:i[i.length-1].last_line,first_column:i[i.length-(x||1)].first_column,last_column:i[i.length-1].last_column},u&&(k._$.range=[i[i.length-(x||1)].range[0],i[i.length-1].range[1]]),void 0!==(m=this.performAction.call(k,o,a,h,this.yy,d[1],r,i)))return m;x&&(n=n.slice(0,-1*x*2),r=r.slice(0,-1*x),i=i.slice(0,-1*x)),n.push(this.productions_[d[1]][0]),r.push(k.$),i.push(k._$),w=s[n[n.length-2]][n[n.length-1]],n.push(w);break;case 3:return!0}}return!0}},e=function(){var t={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t){return this._input=t,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e-1),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this},more:function(){return this._more=!0,this},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},next:function(){if(this.done)return this.EOF;var t,e,n,r,i;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var s=this._currentRules(),o=0;oe[0].length)||(e=n,r=o,this.options.flex));o++);return e?((i=e[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=i.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:i?i[i.length-1].length-i[i.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],t=this.performAction.call(this,this.yy,this,s[r],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),t||void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return void 0!==t?t:this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(t){this.begin(t)},options:{},performAction:function(t,e,n,r){switch(n){case 0:return"*";case 1:return"/";case 2:return"-";case 3:return"+";case 4:return"^";case 5:return"%";case 6:return"(";case 7:return")";case 8:return",";case 9:return"==";case 10:return"!=";case 11:return"~=";case 12:return">=";case 13:return"<=";case 14:return"<";case 15:return">";case 16:return"and";case 17:return"or";case 18:return"not";case 19:return"in";case 20:return"of";case 21:return"if";case 22:return"then";case 23:return"else";case 24:break;case 25:return"NUMBER";case 26:return e.yytext=JSON.stringify(e.yytext),"SYMBOL";case 27:return e.yytext=t.buildString("'",e.yytext),"SYMBOL";case 28:return e.yytext=t.buildString('"',e.yytext),"STRING";case 29:return"EOF"}},rules:[/^(?:\*)/,/^(?:\/)/,/^(?:-)/,/^(?:\+)/,/^(?:\^)/,/^(?:\%)/,/^(?:\()/,/^(?:\))/,/^(?:\,)/,/^(?:==)/,/^(?:\!=)/,/^(?:\~=)/,/^(?:>=)/,/^(?:<=)/,/^(?:<)/,/^(?:>)/,/^(?:and[^\w])/,/^(?:or[^\w])/,/^(?:not[^\w])/,/^(?:in[^\w])/,/^(?:of[^\w])/,/^(?:if[^\w])/,/^(?:then[^\w])/,/^(?:else[^\w])/,/^(?:\s+)/,/^(?:[0-9]+(?:\.[0-9]+)?\b)/,/^(?:[a-zA-Z$_][\.a-zA-Z0-9$_]*)/,/^(?:'(?:\\'|\\\\|[^'\\])*')/,/^(?:"(?:\\"|\\\\|[^"\\])*")/,/^(?:$)/],conditions:{INITIAL:{rules:[0,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],inclusive:!0}}};return t}();function n(){this.yy={}}return t.lexer=e,n.prototype=t,t.Parser=n,new n}();const n=e;function r(t,e){return("object"==typeof t||"function"==typeof t)&&Object.prototype.hasOwnProperty.call(t,e)}e.Parser;const i="function"!=typeof Symbol||"symbol"!=typeof Symbol.toPrimitive?()=>{}:t=>{if("function"==typeof t[Symbol.toPrimitive])return t[Symbol.toPrimitive]};function s(t,e){if("object"==typeof t&&"function"==typeof t)try{let n=i(t)(e);if(typeof n===e)return n;if(n=t.valueOf(),typeof n===e)return n}finally{return}}function o(t,e){return(t%e+e)%e}function h(t){const e=t;if(null==t)throw new TypeError(`Expected a numeric value, but got ${t} instead.`);if(Array.isArray(t)&&1===t.length&&(t=t[0]),"object"==typeof t&&(t=s(t,"number")),"number"==typeof t)return t;throw new TypeError(`Expected a numeric value, but got an ${typeof e} instead.`)}function a(t){const e=t;if(null==t)throw new TypeError(`Expected a text, but got ${t} instead.`);if(Array.isArray(t)&&1===t.length&&(t=t[0]),"object"==typeof t&&(t=s(t,"string")),"string"==typeof t)return t;throw new TypeError(`Expected a text, but got an ${typeof e} instead.`)}function l(t){const e=t;let n;if("string"==typeof t||"number"==typeof t)return t;if(null==t)throw new TypeError(`Expected a numeric value, but got ${t} instead.`);if(Array.isArray(t)&&1===t.length&&(t=t[0]),"object"==typeof t){if(n=s(t,"number"),"number"==typeof n)return n;if(n=s(t,"string"),"string"==typeof n)return n}throw new TypeError(`Expected a text or a numeric value, but got an ${typeof e} instead.`)}function c(t){if(null==t)throw new TypeError(`Expected a list, but got ${t} instead.`);return Array.isArray(t)?t:[t]}const u={isfn:function(t,e){return r(t,e)&&"function"==typeof t[e]},unknown:function(t){throw new ReferenceError("Unknown function: "+t+"()")},coerceArray:c,coerceNumber:h,coerceNumberOrString:l,coerceBoolean:function(t){if("boolean"==typeof t)return t;if("object"==typeof t&&t instanceof Boolean)return t.valueOf();throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof t} instead.`)},isSubset:function(t,e){const n=c(t),r=c(e);return n.every(t=>r.includes(t))},buildString:function(t,e){t=String(t)[0];let n="";if((e=String(e))[0]!==t||e[e.length-1]!==t)throw new Error("Unexpected internal error: String literal doesn't begin/end with the right quotation mark.");for(let r=1;r=e.length-1)throw new Error("Unexpected internal error: Unescaped backslash at the end of string literal.");if("\\"===e[r])n+="\\";else{if(e[r]!==t)throw new Error("Unexpected internal error: Invalid escaped character in string literal: "+e[r]);n+=t}}else{if(e[r]===t)throw new Error("Unexpected internal error: String literal contains unescaped quotation mark.");n+=e[r]}return JSON.stringify(n)}};return n.yy=Object.create(u),t.compileExpression=function(t,e){if(arguments.length>2)throw new TypeError("Too many arguments.");e="object"==typeof e?e:{};let{extraFunctions:i,customProp:s,operators:c}=e;for(const t of Object.keys(e))if(!["extraFunctions","customProp","operators"].includes(t))throw new TypeError("Unknown option: "+t);let y={abs:Math.abs,ceil:Math.ceil,floor:Math.floor,log:Math.log,max:Math.max,min:Math.min,random:Math.random,round:Math.round,sqrt:Math.sqrt,exists:t=>null!=t,empty:t=>null==t||""===t||Array.isArray(t)&&0===t.length};if(i)for(const t of Object.keys(i))y[t]=i[t];let f={"+":(t,e)=>l(t)+l(e),"-":(t,e)=>void 0===e?-h(t):h(t)-h(e),"*":(t,e)=>h(t)*h(e),"/":(t,e)=>h(t)/h(e),"%":(t,e)=>o(h(t),h(e)),"^":(t,e)=>Math.pow(h(t),h(e)),"==":(t,e)=>t===e,"!=":(t,e)=>t!==e,"<":(t,e)=>h(t)h(t)<=h(e),">=":(t,e)=>h(t)>=h(e),">":(t,e)=>h(t)>h(e),"~=":(t,e)=>RegExp(a(e)).test(a(t))};if(c)for(const t of Object.keys(c))f[t]=c[t];c=f;let p=n.parse(t),g=[];function d(t){Array.isArray(t)?t.forEach(d):g.push(t)}function m(t,e){if(r(e||{},t))return e[t];throw new ReferenceError(`Property “${t}” does not exist.`)}function b(t){return function(e){if(r(t||{},e))return t[e];throw new ReferenceError(`Property “${e}” does not exist.`)}}g.push("return "),p.forEach(d),g.push(";"),"function"==typeof s&&(m=(t,e)=>s(t,b(e),e));let x=new Function("fns","ops","std","prop","data",g.join(""));return function(t){try{return x(y,c,u,m,t)}catch(t){return t}}},Object.defineProperty(t,"__esModule",{value:!0}),t}({}); \ No newline at end of file diff --git a/dist/cjs/filtrex.js b/dist/cjs/filtrex.js index afa3584..6920c29 100644 --- a/dist/cjs/filtrex.js +++ b/dist/cjs/filtrex.js @@ -119,58 +119,58 @@ var _parser = (function() { case 1: return $$[$0 - 1]; case 2: - this.$ = ["(", "", $$[$0 - 2], " + ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 3: - this.$ = ["(", "", $$[$0 - 2], " - ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 4: - this.$ = ["(", "", $$[$0 - 2], " * ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 5: - this.$ = ["(", "", $$[$0 - 2], " / ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 6: - this.$ = ["(", "", $$[$0 - 2], " % ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 7: - this.$ = ["(", "Math.pow( ", $$[$0 - 2], ", ", $$[$0], " )", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 8: this.$ = ["(", "- ", $$[$0], "", ")"]; break; case 9: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 2], ") && ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 2], " && ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 10: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 2], ") || ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 2], " || ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 11: - this.$ = ["(", "! ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "! ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 12: - this.$ = ["(", "", $$[$0 - 2], " === ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 13: - this.$ = ["(", "", $$[$0 - 2], " !== ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 14: - this.$ = ["(", "RegExp(", $$[$0], ").test(", $$[$0 - 2], ")", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 15: - this.$ = ["(", "", $$[$0 - 2], " < ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 16: - this.$ = ["(", "", $$[$0 - 2], " <= ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 17: - this.$ = ["(", "", $$[$0 - 2], " > ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 18: - this.$ = ["(", "", $$[$0 - 2], " >= ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 19: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 4], ") ? ", $$[$0 - 2], " : ", $$[$0], "", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 4], " ? ", $$[$0 - 2], " : ", $$[$0], "", ")"]; break; case 20: this.$ = ["(", "", $$[$0 - 1], "", ")"]; @@ -1794,63 +1794,130 @@ function toPrimitive(obj, type) { } } -// the parser is dynamically generated from generateParser.js at compile time -// Shared utility functions -const std = -{ +/** + * Mathematically correct modulo + * @param {number} a + * @param {number} b + * @returns {number} + */ - isfn: function(fns, funcName) { - return hasOwnProperty(fns, funcName) && typeof fns[funcName] === "function"; - }, +function mod(a, b) { + return (a % b + b) % b +} - unknown: function(funcName) { - throw new ReferenceError('Unknown function: ' + funcName + '()'); - }, - coerceArray: function(value) { - if (value === undefined || value === null) { - throw new TypeError(`Expected a list, but got ${value} instead.`) - } - if (Array.isArray(value)) { - return value; - } else { - return [value]; - } - }, +// Type assertions/coertions - coerceNumber: function (value) { - const origValue = value; +function num(value) { + const origValue = value; - if (value === undefined || value === null) - throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) - if (Array.isArray(value) && value.length === 1) - value = value[0]; + if (Array.isArray(value) && value.length === 1) + value = value[0]; - if (typeof value === 'object') - value = toPrimitive(value); + if (typeof value === 'object') + value = toPrimitive(value, 'number'); - if (typeof value === 'number' || typeof value === 'bigint') - return value; + if (typeof value === 'number') + return value; - throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) - }, + throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) +} + +function str(value) { + const origValue = value; + + if (value === undefined || value === null) + throw new TypeError(`Expected a text, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0]; + + if (typeof value === 'object') + value = toPrimitive(value, 'string'); + + if (typeof value === 'string') + return value; + + throw new TypeError(`Expected a text, but got an ${typeof origValue} instead.`) +} + +function numstr(value) { + const origValue = value; + let converted; + + if (typeof value === 'string' || typeof value === 'number') + return value + + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0]; + + if (typeof value === 'object') { + converted = toPrimitive(value, 'number'); + + if (typeof converted === 'number') + return converted; + + converted = toPrimitive(value, 'string'); + + if (typeof converted === 'string') + return converted; + } + + throw new TypeError(`Expected a text or a numeric value, but got an ${typeof origValue} instead.`) +} + +function bool(value) { + if (typeof value === 'boolean') + return value + + if (typeof value === 'object' && value instanceof Boolean) + return value.valueOf(); + + throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) +} + +function arr(value) { + if (value === undefined || value === null) { + throw new TypeError(`Expected a list, but got ${value} instead.`) + } - coerceBoolean: function(value) { - if (typeof value === 'boolean') - return value + if (Array.isArray(value)) { + return value; + } else { + return [value]; + } +} + +// the parser is dynamically generated from generateParser.js at compile time + +// Shared utility functions +const std = +{ - if (typeof value === 'object' && value instanceof Boolean) - return value.valueOf(); + isfn: function(fns, funcName) { + return hasOwnProperty(fns, funcName) && typeof fns[funcName] === "function"; + }, - throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) + unknown: function(funcName) { + throw new ReferenceError('Unknown function: ' + funcName + '()'); }, + coerceArray: arr, + coerceNumber: num, + coerceNumberOrString: numstr, + coerceBoolean: bool, + isSubset: function(a, b) { - const A = std.coerceArray(a); - const B = std.coerceArray(b); + const A = arr(a); + const B = arr(b); return A.every( val => B.includes(val) ); }, @@ -1907,10 +1974,11 @@ function compileExpression(expression, options) { if (arguments.length > 2) throw new TypeError('Too many arguments.'); options = typeof options === "object" ? options : {}; - let {extraFunctions, customProp} = options; - for (let key of Object.getOwnPropertyNames(options)) + let {extraFunctions, customProp, operators} = options; + for (const key of Object.keys(options)) { - if (key !== "extraFunctions" && key !== "customProp") throw new TypeError(`Unknown option: ${key}`); + if (!(["extraFunctions", "customProp", "operators"].includes(key))) + throw new TypeError(`Unknown option: ${key}`); } @@ -1932,13 +2000,39 @@ function compileExpression(expression, options) { }; if (extraFunctions) { - for (var name in extraFunctions) { - if (hasOwnProperty(extraFunctions, name)) { - functions[name] = extraFunctions[name]; - } + for (const name of Object.keys(extraFunctions)) { + functions[name] = extraFunctions[name]; } } + let defaultOperators = { + '+': (a, b) => numstr(a) + numstr(b), + '-': (a, b) => b === undefined ? -num(a) : num(a) - num(b), + '*': (a, b) => num(a) * num(b), + '/': (a, b) => num(a) / num(b), + + '%': (a, b) => mod(num(a), num(b)), + '^': (a, b) => Math.pow(num(a), num(b)), + + '==': (a, b) => a === b, + '!=': (a, b) => a !== b, + + '<': (a, b) => num(a) < num(b), + '<=': (a, b) => num(a) <= num(b), + '>=': (a, b) => num(a) >= num(b), + '>': (a, b) => num(a) > num(b), + + '~=': (a, b) => RegExp(str(b)).test(str(a)) + }; + + if (operators) { + for (const name of Object.keys(operators)) { + defaultOperators[name] = operators[name]; + } + } + + operators = defaultOperators; + // Compile the expression @@ -1985,11 +2079,11 @@ function compileExpression(expression, options) { // Patch together and return - let func = new Function('fns', 'std', 'prop', 'data', js.join('')); + let func = new Function('fns', 'ops', 'std', 'prop', 'data', js.join('')); return function(data) { try { - return func(functions, std, prop, data); + return func(functions, operators, std, prop, data); } catch (e) { diff --git a/dist/esm/filtrex.d.ts b/dist/esm/filtrex.d.ts index b64860c..0c3be49 100644 --- a/dist/esm/filtrex.d.ts +++ b/dist/esm/filtrex.d.ts @@ -36,7 +36,7 @@ * * `x or y` Boolean or * * `x and y` Boolean and * * `not x` Boolean not - * * `x ? y : z` If boolean x, value y, else z + * * `if x then y else z` If boolean x, value y, else z * * `( x )` Explicity operator precedence * * `( x, y, z )` Array of elements x, y and z * * `abs(x)` Absolute value @@ -64,7 +64,7 @@ export interface Options */ extraFunctions?: { [T: string]: Function - }, + } /** * If you want to do some more magic with your expression, you can supply a custom function @@ -89,13 +89,38 @@ export interface Options * (word, _, string) => containsWord(string, word) * ); * - * myfilter("Bob is boring"); // returns 0 - * myfilter("Bob met Alice"); // returns 1 - * myfilter("Cecil is cool"); // returns 1 + * myfilter("Bob is boring"); // returns false + * myfilter("Bob met Alice"); // returns true + * myfilter("Cecil is cool"); // returns true */ customProp?: ( name: string, get: (name: string) => any, object: any ) => any + + /** + * This option lets you override operators like `+` and `>=` with custom functions. + */ + operators?: Operators +} + +export interface Operators { + '+'?: (a: any, b: any) => any + '-'?: (a: any, b?: any) => any + '*'?: (a: any, b: any) => any + '/'?: (a: any, b: any) => any + + '%'?: (a: any, b: any) => any + '^'?: (a: any, b: any) => any + + '==': (a: any, b: any) => boolean + '!=': (a: any, b: any) => boolean + + '<'?: (a: any, b: any) => boolean + '>='?: (a: any, b: any) => boolean + '<='?: (a: any, b: any) => boolean + '>'?: (a: any, b: any) => boolean + + '~='?: (a: any, b: any) => boolean } diff --git a/dist/esm/filtrex.mjs b/dist/esm/filtrex.mjs index 06613b6..3a9141e 100644 --- a/dist/esm/filtrex.mjs +++ b/dist/esm/filtrex.mjs @@ -1,6 +1,6 @@ // the parser is dynamically generated from generateParser.js at compile time import { parser } from './parser.mjs' -import { hasOwnProperty, toPrimitive } from './utils.mjs' +import { hasOwnProperty, bool, num, numstr, mod, arr, str } from './utils.mjs' // Shared utility functions const std = @@ -14,49 +14,14 @@ const std = throw new ReferenceError('Unknown function: ' + funcName + '()'); }, - coerceArray: function(value) { - if (value === undefined || value === null) { - throw new TypeError(`Expected a list, but got ${value} instead.`) - } - - if (Array.isArray(value)) { - return value; - } else { - return [value]; - } - }, - - coerceNumber: function (value) { - const origValue = value - - if (value === undefined || value === null) - throw new TypeError(`Expected a numeric value, but got ${value} instead.`) - - if (Array.isArray(value) && value.length === 1) - value = value[0] - - if (typeof value === 'object') - value = toPrimitive(value) - - if (typeof value === 'number' || typeof value === 'bigint') - return value; - - throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) - }, - - coerceBoolean: function(value) { - if (typeof value === 'boolean') - return value - - if (typeof value === 'object' && value instanceof Boolean) - return value.valueOf(); - - throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) - }, + coerceArray: arr, + coerceNumber: num, + coerceNumberOrString: numstr, + coerceBoolean: bool, isSubset: function(a, b) { - const A = std.coerceArray(a); - const B = std.coerceArray(b); + const A = arr(a); + const B = arr(b); return A.every( val => B.includes(val) ); }, @@ -113,10 +78,11 @@ export function compileExpression(expression, options) { if (arguments.length > 2) throw new TypeError('Too many arguments.'); options = typeof options === "object" ? options : {}; - let {extraFunctions, customProp} = options; - for (let key of Object.getOwnPropertyNames(options)) + let {extraFunctions, customProp, operators} = options; + for (const key of Object.keys(options)) { - if (key !== "extraFunctions" && key !== "customProp") throw new TypeError(`Unknown option: ${key}`); + if (!(["extraFunctions", "customProp", "operators"].includes(key))) + throw new TypeError(`Unknown option: ${key}`); } @@ -138,13 +104,39 @@ export function compileExpression(expression, options) { }; if (extraFunctions) { - for (var name in extraFunctions) { - if (hasOwnProperty(extraFunctions, name)) { - functions[name] = extraFunctions[name]; - } + for (const name of Object.keys(extraFunctions)) { + functions[name] = extraFunctions[name]; } } + let defaultOperators = { + '+': (a, b) => numstr(a) + numstr(b), + '-': (a, b) => b === undefined ? -num(a) : num(a) - num(b), + '*': (a, b) => num(a) * num(b), + '/': (a, b) => num(a) / num(b), + + '%': (a, b) => mod(num(a), num(b)), + '^': (a, b) => Math.pow(num(a), num(b)), + + '==': (a, b) => a === b, + '!=': (a, b) => a !== b, + + '<': (a, b) => num(a) < num(b), + '<=': (a, b) => num(a) <= num(b), + '>=': (a, b) => num(a) >= num(b), + '>': (a, b) => num(a) > num(b), + + '~=': (a, b) => RegExp(str(b)).test(str(a)) + }; + + if (operators) { + for (const name of Object.keys(operators)) { + defaultOperators[name] = operators[name]; + } + } + + operators = defaultOperators + // Compile the expression @@ -191,11 +183,11 @@ export function compileExpression(expression, options) { // Patch together and return - let func = new Function('fns', 'std', 'prop', 'data', js.join('')); + let func = new Function('fns', 'ops', 'std', 'prop', 'data', js.join('')); return function(data) { try { - return func(functions, std, prop, data); + return func(functions, operators, std, prop, data); } catch (e) { diff --git a/dist/esm/parser.mjs b/dist/esm/parser.mjs index 785c06e..3d6ed29 100644 --- a/dist/esm/parser.mjs +++ b/dist/esm/parser.mjs @@ -116,58 +116,58 @@ var _parser = (function() { return $$[$0 - 1]; break; case 2: - this.$ = ["(", "", $$[$0 - 2], " + ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 3: - this.$ = ["(", "", $$[$0 - 2], " - ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 4: - this.$ = ["(", "", $$[$0 - 2], " * ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 5: - this.$ = ["(", "", $$[$0 - 2], " / ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 6: - this.$ = ["(", "", $$[$0 - 2], " % ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 7: - this.$ = ["(", "Math.pow( ", $$[$0 - 2], ", ", $$[$0], " )", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 8: this.$ = ["(", "- ", $$[$0], "", ")"]; break; case 9: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 2], ") && ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 2], " && ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 10: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 2], ") || ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 2], " || ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 11: - this.$ = ["(", "! ", "std.coerceBoolean", "(", $$[$0], ")", ")"]; + this.$ = ["(", "! ", "std.coerceBoolean", "", $$[$0], "", ")"]; break; case 12: - this.$ = ["(", "", $$[$0 - 2], " === ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 13: - this.$ = ["(", "", $$[$0 - 2], " !== ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 14: - this.$ = ["(", "RegExp(", $$[$0], ").test(", $$[$0 - 2], ")", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 15: - this.$ = ["(", "", $$[$0 - 2], " < ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 16: - this.$ = ["(", "", $$[$0 - 2], " <= ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 17: - this.$ = ["(", "", $$[$0 - 2], " > ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 18: - this.$ = ["(", "", $$[$0 - 2], " >= ", $$[$0], "", ")"]; + this.$ = ["(", "ops['", $$[$0 - 1], "'](", $$[$0 - 2], ", ", $$[$0], ")", ")"]; break; case 19: - this.$ = ["(", "", "std.coerceBoolean", "(", $$[$0 - 4], ") ? ", $$[$0 - 2], " : ", $$[$0], "", ")"]; + this.$ = ["(", "", "std.coerceBoolean", "", $$[$0 - 4], " ? ", $$[$0 - 2], " : ", $$[$0], "", ")"]; break; case 20: this.$ = ["(", "", $$[$0 - 1], "", ")"]; diff --git a/dist/esm/utils.mjs b/dist/esm/utils.mjs index db17884..4623c0e 100644 --- a/dist/esm/utils.mjs +++ b/dist/esm/utils.mjs @@ -48,3 +48,104 @@ export function toPrimitive(obj, type) { } } + +/** + * Mathematically correct modulo + * @param {number} a + * @param {number} b + * @returns {number} + */ + +export function mod(a, b) { + return (a % b + b) % b +} + + + +// Type assertions/coertions + +export function num(value) { + const origValue = value + + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0] + + if (typeof value === 'object') + value = toPrimitive(value, 'number') + + if (typeof value === 'number') + return value; + + throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) +} + +export function str(value) { + const origValue = value + + if (value === undefined || value === null) + throw new TypeError(`Expected a text, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0] + + if (typeof value === 'object') + value = toPrimitive(value, 'string') + + if (typeof value === 'string') + return value; + + throw new TypeError(`Expected a text, but got an ${typeof origValue} instead.`) +} + +export function numstr(value) { + const origValue = value + let converted + + if (typeof value === 'string' || typeof value === 'number') + return value + + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0] + + if (typeof value === 'object') { + converted = toPrimitive(value, 'number') + + if (typeof converted === 'number') + return converted; + + converted = toPrimitive(value, 'string') + + if (typeof converted === 'string') + return converted; + } + + throw new TypeError(`Expected a text or a numeric value, but got an ${typeof origValue} instead.`) +} + +export function bool(value) { + if (typeof value === 'boolean') + return value + + if (typeof value === 'object' && value instanceof Boolean) + return value.valueOf(); + + throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) +} + +export function arr(value) { + if (value === undefined || value === null) { + throw new TypeError(`Expected a list, but got ${value} instead.`) + } + + if (Array.isArray(value)) { + return value; + } else { + return [value]; + } +} diff --git a/src/filtrex.d.ts b/src/filtrex.d.ts index b64860c..0c3be49 100644 --- a/src/filtrex.d.ts +++ b/src/filtrex.d.ts @@ -36,7 +36,7 @@ * * `x or y` Boolean or * * `x and y` Boolean and * * `not x` Boolean not - * * `x ? y : z` If boolean x, value y, else z + * * `if x then y else z` If boolean x, value y, else z * * `( x )` Explicity operator precedence * * `( x, y, z )` Array of elements x, y and z * * `abs(x)` Absolute value @@ -64,7 +64,7 @@ export interface Options */ extraFunctions?: { [T: string]: Function - }, + } /** * If you want to do some more magic with your expression, you can supply a custom function @@ -89,13 +89,38 @@ export interface Options * (word, _, string) => containsWord(string, word) * ); * - * myfilter("Bob is boring"); // returns 0 - * myfilter("Bob met Alice"); // returns 1 - * myfilter("Cecil is cool"); // returns 1 + * myfilter("Bob is boring"); // returns false + * myfilter("Bob met Alice"); // returns true + * myfilter("Cecil is cool"); // returns true */ customProp?: ( name: string, get: (name: string) => any, object: any ) => any + + /** + * This option lets you override operators like `+` and `>=` with custom functions. + */ + operators?: Operators +} + +export interface Operators { + '+'?: (a: any, b: any) => any + '-'?: (a: any, b?: any) => any + '*'?: (a: any, b: any) => any + '/'?: (a: any, b: any) => any + + '%'?: (a: any, b: any) => any + '^'?: (a: any, b: any) => any + + '==': (a: any, b: any) => boolean + '!=': (a: any, b: any) => boolean + + '<'?: (a: any, b: any) => boolean + '>='?: (a: any, b: any) => boolean + '<='?: (a: any, b: any) => boolean + '>'?: (a: any, b: any) => boolean + + '~='?: (a: any, b: any) => boolean } diff --git a/src/filtrex.mjs b/src/filtrex.mjs index 06613b6..3a9141e 100644 --- a/src/filtrex.mjs +++ b/src/filtrex.mjs @@ -1,6 +1,6 @@ // the parser is dynamically generated from generateParser.js at compile time import { parser } from './parser.mjs' -import { hasOwnProperty, toPrimitive } from './utils.mjs' +import { hasOwnProperty, bool, num, numstr, mod, arr, str } from './utils.mjs' // Shared utility functions const std = @@ -14,49 +14,14 @@ const std = throw new ReferenceError('Unknown function: ' + funcName + '()'); }, - coerceArray: function(value) { - if (value === undefined || value === null) { - throw new TypeError(`Expected a list, but got ${value} instead.`) - } - - if (Array.isArray(value)) { - return value; - } else { - return [value]; - } - }, - - coerceNumber: function (value) { - const origValue = value - - if (value === undefined || value === null) - throw new TypeError(`Expected a numeric value, but got ${value} instead.`) - - if (Array.isArray(value) && value.length === 1) - value = value[0] - - if (typeof value === 'object') - value = toPrimitive(value) - - if (typeof value === 'number' || typeof value === 'bigint') - return value; - - throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) - }, - - coerceBoolean: function(value) { - if (typeof value === 'boolean') - return value - - if (typeof value === 'object' && value instanceof Boolean) - return value.valueOf(); - - throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) - }, + coerceArray: arr, + coerceNumber: num, + coerceNumberOrString: numstr, + coerceBoolean: bool, isSubset: function(a, b) { - const A = std.coerceArray(a); - const B = std.coerceArray(b); + const A = arr(a); + const B = arr(b); return A.every( val => B.includes(val) ); }, @@ -113,10 +78,11 @@ export function compileExpression(expression, options) { if (arguments.length > 2) throw new TypeError('Too many arguments.'); options = typeof options === "object" ? options : {}; - let {extraFunctions, customProp} = options; - for (let key of Object.getOwnPropertyNames(options)) + let {extraFunctions, customProp, operators} = options; + for (const key of Object.keys(options)) { - if (key !== "extraFunctions" && key !== "customProp") throw new TypeError(`Unknown option: ${key}`); + if (!(["extraFunctions", "customProp", "operators"].includes(key))) + throw new TypeError(`Unknown option: ${key}`); } @@ -138,13 +104,39 @@ export function compileExpression(expression, options) { }; if (extraFunctions) { - for (var name in extraFunctions) { - if (hasOwnProperty(extraFunctions, name)) { - functions[name] = extraFunctions[name]; - } + for (const name of Object.keys(extraFunctions)) { + functions[name] = extraFunctions[name]; } } + let defaultOperators = { + '+': (a, b) => numstr(a) + numstr(b), + '-': (a, b) => b === undefined ? -num(a) : num(a) - num(b), + '*': (a, b) => num(a) * num(b), + '/': (a, b) => num(a) / num(b), + + '%': (a, b) => mod(num(a), num(b)), + '^': (a, b) => Math.pow(num(a), num(b)), + + '==': (a, b) => a === b, + '!=': (a, b) => a !== b, + + '<': (a, b) => num(a) < num(b), + '<=': (a, b) => num(a) <= num(b), + '>=': (a, b) => num(a) >= num(b), + '>': (a, b) => num(a) > num(b), + + '~=': (a, b) => RegExp(str(b)).test(str(a)) + }; + + if (operators) { + for (const name of Object.keys(operators)) { + defaultOperators[name] = operators[name]; + } + } + + operators = defaultOperators + // Compile the expression @@ -191,11 +183,11 @@ export function compileExpression(expression, options) { // Patch together and return - let func = new Function('fns', 'std', 'prop', 'data', js.join('')); + let func = new Function('fns', 'ops', 'std', 'prop', 'data', js.join('')); return function(data) { try { - return func(functions, std, prop, data); + return func(functions, operators, std, prop, data); } catch (e) { diff --git a/src/generateParser.js b/src/generateParser.js index 1245d96..21b2639 100644 --- a/src/generateParser.js +++ b/src/generateParser.js @@ -28,7 +28,7 @@ function parenless(fragments, ...params) { } const bool = "std.coerceBoolean" -const num = "std.coerceNumber" +const operatorCode = code`ops['${2}'](${1}, ${3})` const grammar = { // Lexical tokens @@ -106,24 +106,24 @@ const grammar = { ['e EOF', 'return $1;'] ], e: [ - ['e + e' , code`${1} + ${3}`], - ['e - e' , code`${1} - ${3}`], - ['e * e' , code`${1} * ${3}`], - ['e / e' , code`${1} / ${3}`], - ['e % e' , code`${1} % ${3}`], - ['e ^ e' , code`Math.pow( ${1}, ${3} )`], + ['e + e' , operatorCode], + ['e - e' , operatorCode], + ['e * e' , operatorCode], + ['e / e' , operatorCode], + ['e % e' , operatorCode], + ['e ^ e' , operatorCode], ['- e' , code`- ${2}`, {prec: 'UMINUS'}], - ['e and e', code`${bool}(${1}) && ${bool}(${3})`], - ['e or e' , code`${bool}(${1}) || ${bool}(${3})`], - ['not e' , code`! ${bool}(${2})`], - ['e == e' , code`${1} === ${3}`], - ['e != e' , code`${1} !== ${3}`], - ['e ~= e' , code`RegExp(${3}).test(${1})`], - ['e < e' , code`${1} < ${3}`], - ['e <= e' , code`${1} <= ${3}`], - ['e > e' , code`${1} > ${3}`], - ['e >= e' , code`${1} >= ${3}`], - ['if e then e else e', code`${bool}(${2}) ? ${4} : ${6}`], + ['e and e', code`${bool}${1} && ${bool}${3}`], + ['e or e' , code`${bool}${1} || ${bool}${3}`], + ['not e' , code`! ${bool}${2}`], + ['e == e' , operatorCode], + ['e != e' , operatorCode], + ['e ~= e' , operatorCode], + ['e < e' , operatorCode], + ['e <= e' , operatorCode], + ['e > e' , operatorCode], + ['e >= e' , operatorCode], + ['if e then e else e', code`${bool}${2} ? ${4} : ${6}`], ['( e )' , code`${2}`], ['( array , e )', code`[ ${2}, ${4} ]`], ['NUMBER' , code`${1}`], diff --git a/src/utils.mjs b/src/utils.mjs index db17884..4623c0e 100644 --- a/src/utils.mjs +++ b/src/utils.mjs @@ -48,3 +48,104 @@ export function toPrimitive(obj, type) { } } + +/** + * Mathematically correct modulo + * @param {number} a + * @param {number} b + * @returns {number} + */ + +export function mod(a, b) { + return (a % b + b) % b +} + + + +// Type assertions/coertions + +export function num(value) { + const origValue = value + + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0] + + if (typeof value === 'object') + value = toPrimitive(value, 'number') + + if (typeof value === 'number') + return value; + + throw new TypeError(`Expected a numeric value, but got an ${typeof origValue} instead.`) +} + +export function str(value) { + const origValue = value + + if (value === undefined || value === null) + throw new TypeError(`Expected a text, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0] + + if (typeof value === 'object') + value = toPrimitive(value, 'string') + + if (typeof value === 'string') + return value; + + throw new TypeError(`Expected a text, but got an ${typeof origValue} instead.`) +} + +export function numstr(value) { + const origValue = value + let converted + + if (typeof value === 'string' || typeof value === 'number') + return value + + if (value === undefined || value === null) + throw new TypeError(`Expected a numeric value, but got ${value} instead.`) + + if (Array.isArray(value) && value.length === 1) + value = value[0] + + if (typeof value === 'object') { + converted = toPrimitive(value, 'number') + + if (typeof converted === 'number') + return converted; + + converted = toPrimitive(value, 'string') + + if (typeof converted === 'string') + return converted; + } + + throw new TypeError(`Expected a text or a numeric value, but got an ${typeof origValue} instead.`) +} + +export function bool(value) { + if (typeof value === 'boolean') + return value + + if (typeof value === 'object' && value instanceof Boolean) + return value.valueOf(); + + throw new TypeError(`Expected a boolean (“true” or “false”) value, but got an ${typeof value} instead.`) +} + +export function arr(value) { + if (value === undefined || value === null) { + throw new TypeError(`Expected a list, but got ${value} instead.`) + } + + if (Array.isArray(value)) { + return value; + } else { + return [value]; + } +}