UNPKG

covertable

Version:

Efficient TypeScript library for pairwise testing, generating minimal covering arrays with constraint support.

10 lines (9 loc) 14.5 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const T=require("./controller-tIzPuNQH.cjs");class W extends Error{constructor(t){const n=t.filter(r=>r.severity==="error"),o=n.map(r=>` [${r.source}#${r.index} line ${r.line}] ${r.message}`).join(` `);super(`PictModel has ${n.length} error(s): ${o}`),this.name="PictModelError",this.issues=t}}function K(l){const t=[];let n="",o=!1;for(const r of l)r==='"'?(o=!o,n+=r):r===","&&!o?(n.trim()&&t.push(n.trim()),n=""):n+=r;return n.trim()&&t.push(n.trim()),t}function $(l){return l.startsWith('"')&&l.endsWith('"')?l.slice(1,-1):l}function k(l,t,n){const o=l.trim();if(o.startsWith("<")&&o.endsWith(">")){const E=o.slice(1,-1);if(!(E in t))throw new Error(`Unknown parameter reference: "${E}"`);return{values:[...t[E]],isNegative:!1,weight:1}}let r=o,a=!1,i=1;r.startsWith("~")&&(r=r.slice(1),a=!0);const p=r.match(/\s*\((\d+)\)\s*$/);if(p&&(i=parseInt(p[1],10),r=r.slice(0,r.lastIndexOf("(")).trim()),r.includes("|")){const E=r.split("|").map(y=>y.trim()),v=$(E[0]);for(let y=1;y<E.length;y++){const d=$(E[y]);n.set(d,v)}r=E[0]}if(r.startsWith('"')&&r.endsWith('"'))return{values:[r.slice(1,-1)],isNegative:a,weight:i};const h=Number(r);return{values:[r!==""&&!isNaN(h)?h:r],isNegative:a,weight:i}}function F(l){return!(l.indexOf(":")<=0||l.startsWith("[")||/^IF\s/i.test(l))}function j(l){const t={},n=new Map,o=new Map,r={},a=[];let i=0;const p=(h,E)=>{a.push({severity:"error",source:"factor",index:i,line:h,message:E})};for(const{text:h,line:E}of l){const v=h.trim();if(v===""||v.startsWith("#"))continue;const y=v.indexOf(":");if(y===-1){p(E,`Invalid line (missing ":"): ${v}`),i++;continue}const d=v.slice(0,y).trim();if(d===""){p(E,`Empty parameter name in line: ${v}`),i++;continue}try{const u=K(v.slice(y+1)),w=[],N=new Set,O={};for(const A of u){const L=k(A,t,n),_=w.length;if(w.push(...L.values),L.isNegative)for(const x of L.values)N.add(x);if(L.weight!==1)for(let x=0;x<L.values.length;x++)O[_+x]=L.weight}if(w.length===0){p(E,`No values for parameter "${d}"`),i++;continue}t[d]=w,N.size>0&&o.set(d,N),Object.keys(O).length>0&&(r[d]=O)}catch(u){p(E,u.message)}i++}return{factors:t,aliases:n,negatives:o,weights:r,issues:a}}const B=/^\{\s*(.+?)\s*\}\s*@\s*(\d+)\s*$/;function z(l){const t=l.match(B);if(!t)return null;const n=t[1].split(",").map(r=>r.trim()).filter(r=>r!==""),o=parseInt(t[2],10);return{fields:n,strength:o}}function I(l,t){if(l.startsWith("[")&&l.endsWith("]"))return{type:"REF",value:l,line:t};if(l.startsWith('"')&&l.endsWith('"'))return{type:"STRING",value:l,line:t};if(!isNaN(parseFloat(l)))return{type:"NUMBER",value:l,line:t};if(["TRUE","FALSE"].includes(l.toUpperCase()))return{type:"BOOLEAN",value:l.toUpperCase(),line:t};if(l.toUpperCase()==="NULL")return{type:"NULL",value:l.toUpperCase(),line:t};if(["IF","ELSE","THEN"].includes(l.toUpperCase()))return{type:l.toUpperCase(),value:l.toUpperCase(),line:t};if(["=","<>",">","<",">=","<=","IN","LIKE"].includes(l.toUpperCase()))return{type:"COMPARER",value:l.toUpperCase(),line:t};if(["AND","OR","NOT"].includes(l.toUpperCase()))return{type:"OPERATOR",value:l.toUpperCase(),line:t};if(["+","-","*","/","%","^"].includes(l))return{type:"ARITHMETIC",value:l,line:t};if(l==="**")return{type:"ARITHMETIC",value:"^",line:t};switch(l){case"(":return{type:"LPAREN",value:l,line:t};case")":return{type:"RPAREN",value:l,line:t};case"{":return{type:"LBRACE",value:l,line:t};case"}":return{type:"RBRACE",value:l,line:t};case",":return{type:"COMMA",value:l,line:t};case":":return{type:"COLON",value:l,line:t};case";":return{type:"SEMICOLON",value:l,line:t};default:return{type:"UNKNOWN",value:l,line:t}}}const S=l=>l===" "||l===` `||l===" ";class V{constructor(t,n=!1,o=new Map,r=!1,a=1){this.input=t,this.debug=n,this.aliases=o,this.caseInsensitive=r,this.startLine=a,this.tokens=[],this.filters=[],this.errors=[],this.filterLines=[],this.filterKeys=[],this.filter=(i,...p)=>{for(const h of this.filters)if(h!=null&&!h(i))return!1;for(const h of p)if(!h(i))return!1;return!0};try{this.tokenize()}catch(i){this.debug&&console.error("Tokenize error:",i.message),this.errors.push(i.message),this.filterLines.push(this.startLine);return}this.analyze()}tokenize(){const t=this.input,n=[];let o="",r=this.startLine,a=this.startLine,i=!1,p=!1,h=!1;const E=(d,u,w)=>{n.push({type:d,value:u,line:w})},v=()=>{o.length>0&&(n.push(I(o,r)),o="")},y=d=>{o.length===0&&(r=a),o+=d};for(let d=0;d<t.length;d++){const u=t[d];if(u==='"')i=!i,y(u),i||(E("STRING",o,r),o="");else if(i)y(u);else if(u==="[")v(),h=!0,y(u);else if(u==="]"&&h)y(u),n.push(I(o,r)),h=!1,o="";else if(u==="{")p=!0,v(),E("LBRACE",u,a);else if(u==="}")p=!1,v(),E("RBRACE",u,a);else if(u===","&&p)v(),E("COMMA",u,a);else if("+-*/%^".includes(u)&&!p&&!h)v(),u==="*"&&t[d+1]==="*"?(n.push(I("**",a)),d++):n.push(I(u,a));else if("[]=<>!();:".includes(u)&&!p&&!h)if(v(),u==="<"||u===">"||u==="!"||u==="="){const w=t[d+1];w==="="?(n.push(I(u+"=",a)),d++):u==="<"&&w===">"?(n.push(I("<>",a)),d++):n.push(I(u,a))}else n.push(I(u,a));else if(S(u)&&!p&&!h){v();let w=u;for(u===` `&&a++;d+1<t.length&&S(t[d+1]);)d++,w+=t[d],t[d]===` `&&a++;E("WHITESPACE",w,a)}else S(u)&&(p||h)&&u===` `&&a++,y(u)}if(i)throw new Error(`Unterminated string literal: ${o}`);if(h)throw new Error(`Unterminated field reference: ${o}`);if(p)throw new Error('Unterminated set (missing closing "}")');return o.length>0&&n.push(I(o,r)),this.tokens=n,n}analyze(){let t=0;const n=this.tokens,o=this.caseInsensitive,r=s=>o&&typeof s=="string"?s.toLowerCase():s;let a=new Set;const i=()=>{for(;t<n.length&&n[t].type==="WHITESPACE";)t++;return t<n.length?n[t++]:null},p=()=>{let s=h(),e=i();if((e==null?void 0:e.type)==="UNKNOWN")throw new Error(`Unexpected token: ${e.value}`);for(;e&&e.type==="OPERATOR"&&e.value==="OR";){const c=s,g=h();s=f=>c(f)||g(f),e=i()}return t--,s},h=()=>{let s=v(),e=i();if((e==null?void 0:e.type)==="UNKNOWN")throw new Error(`Unexpected token: ${e.value}`);for(;e&&e.type==="OPERATOR"&&e.value==="AND";){const c=s,g=v();s=f=>c(f)&&g(f),e=i()}return t--,s},E=()=>{const s=t;let e=1,c=!1;for(;t<n.length;){const g=n[t++];if(g.type!=="WHITESPACE"){if(g.type==="LPAREN")e++;else if(g.type==="RPAREN"){if(e--,e===0)break}else if(e===1&&g.type==="COMPARER"){c=!0;break}}}return t=s,c},v=()=>{let s=i();if(s!=null){if(s.type==="OPERATOR"&&s.value==="NOT"){const e=v();return c=>!e(c)}if(s.type==="LPAREN")if(E()){const e=p();if(s=i(),!s||s.type!=="RPAREN")throw new Error("Expected closing parenthesis");return e}else return t--,y();if(s.type==="BOOLEAN"){const e=s.value.toUpperCase()==="TRUE";return()=>e}if(s.type==="UNKNOWN")throw new Error(`Unexpected token: ${s.value}`)}return t--,y()},y=()=>{const s=A();if(s==null)throw new Error('Expected field or value after "IF", "THEN", "ELSE"');const e=i();if(["NUMBER","STRING","BOOLEAN","NULL"].includes(e==null?void 0:e.type))throw new Error(`Expected comparison operator but found value: ${e==null?void 0:e.value}`);if((e==null?void 0:e.type)==="THEN")throw new Error("A comparison operator and value are required after the field.");if((e==null?void 0:e.type)==="OPERATOR")throw new Error(`Expected comparison operator but found operator: ${e.value}`);if(!e||e.type!=="COMPARER")throw(e==null?void 0:e.value)==="!="?new Error('"!=" is not supported. Use "<>" for inequality comparison'):new Error(`Unknown comparison operator: ${e==null?void 0:e.value}`);const c=e.value;if(c==="IN"){const f=d();return R=>f.has(r(s(R)))}if(c==="LIKE"){const f=A();if(f==null)throw new Error("Expected string pattern after LIKE");const R=f({});if(typeof R!="string")throw new Error("Expected string pattern after LIKE");const m=R.replace(/\*/g,".*").replace(/\?/g,"."),M=new RegExp("^"+m+"$",o?"i":"");return C=>M.test(s(C))}const g=A();if(g==null)throw new Error("Expected field or value");switch(c){case"=":return f=>r(s(f))===r(g(f));case"<>":return f=>r(s(f))!==r(g(f));case">":return f=>s(f)>g(f);case"<":return f=>s(f)<g(f);case">=":return f=>s(f)>=g(f);case"<=":return f=>s(f)<=g(f);default:throw new Error(`Unknown comparison operator: ${c}`)}},d=()=>{const s=[];let e=i();if(e&&e.type==="LBRACE")for(e=i();e&&e.type!=="RBRACE";){if(e.type==="STRING"){const c=e.value.slice(1,-1),g=o?c.toLowerCase():c,f=this.aliases.get(g)??c;s.push(r(f))}else if(e.type!=="COMMA"&&e.type!=="WHITESPACE")throw new Error(`Unexpected token in array: ${e.value}`);e=i()}else throw new Error(`Expected '{' but found ${e?e.value:"NULL"}`);if(s.length===0)throw new Error("Empty set in IN clause");return new Set(s)},u={"+":(s,e)=>s+e,"-":(s,e)=>s-e,"*":(s,e)=>s*e,"/":(s,e)=>s/e,"%":(s,e)=>s%e,"^":(s,e)=>s**e},w=()=>{const s=i();if(s==null)return null;if(s.type==="LPAREN"){const e=A();if(e==null)throw new Error('Expected expression after "("');const c=i();if(!c||c.type!=="RPAREN")throw new Error('Expected closing ")" in arithmetic expression');return e}else if(s.type==="REF"){const e=s.value.slice(1,-1);return a.add(e),c=>c[e]}else if(s.type==="STRING"){const e=s.value.slice(1,-1),c=o?e.toLowerCase():e,g=this.aliases.get(c)??e;return()=>g}else if(s.type==="NUMBER"){const e=parseFloat(s.value);return()=>e}else if(s.type==="BOOLEAN"){const e=s.value==="TRUE";return()=>e}else return s.type==="NULL"?()=>null:null},N=()=>{let s=w();if(s==null)return null;const e=t,c=i();if(c&&c.type==="ARITHMETIC"&&c.value==="^"){const g=N();if(g==null)throw new Error("Expected operand after '^'");s=((m,M)=>C=>m(C)**M(C))(s,g)}else t=e;return s},O=()=>{let s=N();if(s==null)return null;for(;;){const e=t,c=i();if(c&&c.type==="ARITHMETIC"&&"*/%".includes(c.value)){const g=u[c.value],f=N();if(f==null)throw new Error(`Expected operand after '${c.value}'`);s=((M,C,U)=>b=>U(M(b),C(b)))(s,f,g)}else{t=e;break}}return s},A=()=>{let s=O();if(s==null)return null;for(;;){const e=t,c=i();if(c&&c.type==="ARITHMETIC"&&"+-".includes(c.value)){const g=u[c.value],f=O();if(f==null)throw new Error(`Expected operand after '${c.value}'`);s=((M,C,U)=>b=>U(M(b),C(b)))(s,f,g)}else{t=e;break}}return s},L=()=>{for(;t<n.length&&n[t].type!=="SEMICOLON";)t++};let _=this.startLine;const x=(s,e)=>{s==null?(this.debug&&console.error(`Error[${this.errors.length}]:`,e),this.filters.push(null),this.errors.push(e)):(this.debug&&console.debug(`Filter[${this.filters.length}]: compiled`),this.filters.push(s),this.errors.push(null)),this.filterLines.push(_),this.filterKeys.push(a),a=new Set},P=()=>{try{return p()}catch(s){x(null,s.message)}return t--,L(),null};for(;n[t]!=null;){const s=i();if(s==null)break;if(_=s.line,s.type==="IF"){const e=P();if(e==null)continue;const c=i();if(!c||c.type!=="THEN"){x(null,`Expected "THEN" but found ${c?c.value:"end of input"}`),L();continue}const g=P();if(g==null)continue;const f=i();let R=()=>!0;if(f&&f.type==="ELSE"){const m=P();if(m==null)continue;R=m}else t--;x(m=>e(m)?g(m):R(m),null)}else if(s.type!=="SEMICOLON")if(s.type==="UNKNOWN")x(null,`Unknown token: ${s.value}`),L();else{t--;const e=P();e!=null&&x(e,null)}}}}function q(l){const t=l.split(` `),n=[],o=[];let r=t.length;for(let a=0;a<t.length;a++){const i=t[a],p=i.trim(),h=a+1;if(p===""||p.startsWith("#")){n.push({text:i,line:h});continue}if(F(p))n.push({text:i,line:h});else if(B.test(p))o.push({text:p,line:h});else{r=a;break}}return{parameterLines:n,subModelLines:o,constraintText:t.slice(r).map(a=>a.trim().startsWith("#")?"":a).join(` `),constraintStartLine:r+1}}function H(l,t={}){const{caseInsensitive:n=!0}=t,o=q(l),r=[],{factors:a,aliases:i,negatives:p,weights:h,issues:E}=j(o.parameterLines);r.push(...E);const v=n?new Map(Array.from(i,([u,w])=>[u.toLowerCase(),w])):i,y=[];o.subModelLines.forEach(({text:u,line:w},N)=>{const O=z(u);O?y.push(O):r.push({severity:"error",source:"subModel",index:N,line:w,message:`Invalid sub-model definition: ${u}`})});let d=null;return o.constraintText.trim()&&(d=new V(o.constraintText,!1,v,n,o.constraintStartLine),d.errors.forEach((u,w)=>{if(u==null)return;const N=d.filterLines[w]??o.constraintStartLine;r.push({severity:"error",source:"constraint",index:w,line:N,message:u})})),{factors:a,aliases:i,negatives:p,weights:h,subModels:y,lexer:d,issues:r}}class G{constructor(t,n={}){this._controller=null,this.filter=i=>{let p=!1;for(const[h,E]of this._negatives)if(h in i&&E.has(i[h])){if(p)return!1;p=!0}if(this._lexer){for(const h of this._lexer.filters)if(h!=null&&!h(i))return!1}return!0};const{caseInsensitive:o=!0,strict:r=!1}=n,a=H(t,{caseInsensitive:o});if(this._parameters=a.factors,this._subModels=a.subModels,this._negatives=a.negatives,this._weights=a.weights,this._lexer=a.lexer,this.issues=a.issues,r&&this.issues.some(i=>i.severity==="error"))throw new W(this.issues)}get parameters(){return this._parameters}get subModels(){return this._subModels}get constraints(){return this._modelConstraints()}get negatives(){return this._negatives}get weights(){return this._weights}get progress(){var t;return((t=this._controller)==null?void 0:t.progress)??0}get stats(){var t;return((t=this._controller)==null?void 0:t.stats)??null}_modelConstraints(){const t=[];if(this._lexer){const n=this._lexer.filters,o=this._lexer.filterKeys;for(let r=0;r<n.length;r++){const a=n[r];a!=null&&t.push({operator:"fn",requires:[...o[r]],evaluate:a})}}if(this._negatives.size>0){const n=[...this._negatives.keys()],o=this._negatives;t.push({operator:"fn",requires:n,evaluate:r=>{let a=!1;for(const[i,p]of o)if(p.has(r[i])){if(a)return!1;a=!0}return!0}})}return t}_buildOptions(t={}){const{constraints:n,subModels:o,weights:r,...a}=t,i=[...this._modelConstraints(),...n??[]],p=o??(this._subModels.length>0?this._subModels:void 0),h=r??(Object.keys(this._weights).length>0?this._weights:void 0);return{...a,constraints:i,subModels:p,weights:h}}_applyNegativePrefix(t){if(this._negatives.size===0)return t;const n={...t};for(const[o,r]of this._negatives)r.has(n[o])&&(n[o]=`~${n[o]}`);return n}make(t={}){return this._controller=new T.Controller(this._parameters,this._buildOptions(t)),[...this._controller.makeAsync()].map(n=>this._applyNegativePrefix(n))}*makeAsync(t={}){this._controller=new T.Controller(this._parameters,this._buildOptions(t));for(const n of this._controller.makeAsync())yield this._applyNegativePrefix(n)}}function Q(l,t){const n={};for(const[o,r]of Object.entries(t)){const a=l[o];if(!a)continue;const i={};for(const[p,h]of Object.entries(r)){const E=a.findIndex(v=>String(v)===p);E>=0&&(i[E]=h)}Object.keys(i).length>0&&(n[o]=i)}return n}exports.PictModel=G;exports.PictModelError=W;exports.parse=H;exports.weightsByValue=Q;