Implement evaluateAsync for expressions
This commit is contained in:
1 parent
ae3779c484
commit
7e78336141
2 files changed
+183
-111
No files matched your search
@@ -36,6 +36,21 @@ export function map(exp, callback): Expression {
|
|||||||
return callback(clone || exp);
|
return callback(clone || exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function mapAsync(exp, callback): Promise<Expression> {
|
||||||
|
if (!Array.isArray(exp)) return callback(exp);
|
||||||
|
|
||||||
|
let clone;
|
||||||
|
for (let i = 1; i < exp.length; ++i) {
|
||||||
|
const sub = await mapAsync(exp[i], callback);
|
||||||
|
if (sub !== exp[i]) {
|
||||||
|
clone = clone || exp.slice();
|
||||||
|
clone[i] = sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(clone || exp);
|
||||||
|
}
|
||||||
|
|
||||||
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> {
|
function binaryLeft(operatorsParser, nextParser): parsimmon.Parser<{}> {
|
||||||
return parsimmon.seqMap(
|
return parsimmon.seqMap(
|
||||||
nextParser,
|
nextParser,
|
||||||
|
|||||||
+168
-111
@@ -1,11 +1,11 @@
|
|||||||
import { map, parseLikePattern } from "./expression-parser";
|
import { map, mapAsync, parseLikePattern } from "./expression-parser";
|
||||||
import { booleanCnf } from "./expression-cnf";
|
import { booleanCnf } from "./expression-cnf";
|
||||||
import { naiveDpll } from "./sat-solver";
|
import { naiveDpll } from "./sat-solver";
|
||||||
import { Expression } from "../types";
|
import { Expression } from "../types";
|
||||||
|
|
||||||
const isArray = Array.isArray;
|
const isArray = Array.isArray;
|
||||||
|
|
||||||
const regExpCache = new WeakMap();
|
const regExpCache = new WeakMap<object, RegExp>();
|
||||||
|
|
||||||
const REDUCE_SKIP = {};
|
const REDUCE_SKIP = {};
|
||||||
function reduce(exp, callback): Expression {
|
function reduce(exp, callback): Expression {
|
||||||
@@ -55,6 +55,126 @@ export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp {
|
|||||||
return new RegExp(chars.join(""), flags);
|
return new RegExp(chars.join(""), flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function evalExp(e: Expression): Expression {
|
||||||
|
if (e[0] === "AND") {
|
||||||
|
return reduce(e, (a, b) => {
|
||||||
|
if (!isArray(a)) return a ? b : a;
|
||||||
|
if (!isArray(b)) return b ? a : b;
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
} else if (e[0] === "OR") {
|
||||||
|
return reduce(e, (a, b) => {
|
||||||
|
if (!isArray(a)) return a ? a : b;
|
||||||
|
if (!isArray(b)) return b ? b : a;
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
} else if (e[0] === "NOT") {
|
||||||
|
if (!isArray(e[1])) return !e[1];
|
||||||
|
else if (e[1][0] === "NOT") return e[1][1];
|
||||||
|
} else if (e[0] === "IS NULL") {
|
||||||
|
if (isArray(e[1])) return e;
|
||||||
|
else if (e[1] == null) return true;
|
||||||
|
else return null;
|
||||||
|
} else if (e[0] === "IS NOT NULL") {
|
||||||
|
if (isArray(e[1])) return e;
|
||||||
|
else if (e[1] != null) return true;
|
||||||
|
else return null;
|
||||||
|
} else if (e[0] === "LIKE") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2]) || isArray(e[3])) return e;
|
||||||
|
else if (
|
||||||
|
e[1] == null ||
|
||||||
|
e[2] == null ||
|
||||||
|
((e as any[]).length >= 4 && e[3] == null)
|
||||||
|
)
|
||||||
|
return null;
|
||||||
|
const r = regExpCache.get(e as any[]);
|
||||||
|
if (!r) {
|
||||||
|
likePatternToRegExp(e[2], e[3]);
|
||||||
|
regExpCache.set(e as any[], r);
|
||||||
|
}
|
||||||
|
return r.test(e[1]);
|
||||||
|
} else if (e[0] === "NOT LIKE") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2]) || isArray(e[3])) return e;
|
||||||
|
else if (
|
||||||
|
e[1] == null ||
|
||||||
|
e[2] == null ||
|
||||||
|
((e as any[]).length >= 4 && e[3] == null)
|
||||||
|
)
|
||||||
|
return null;
|
||||||
|
const r = regExpCache.get(e as any[]);
|
||||||
|
if (!r) {
|
||||||
|
likePatternToRegExp(e[2], e[3]);
|
||||||
|
regExpCache.set(e as any[], r);
|
||||||
|
}
|
||||||
|
return !r.test(e[1]);
|
||||||
|
} else if (e[0] === "=") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2])) return e;
|
||||||
|
if (e[1] == null || e[2] == null) return null;
|
||||||
|
return e[1] === e[2];
|
||||||
|
} else if (e[0] === "<>") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2])) return e;
|
||||||
|
if (e[1] == null || e[2] == null) return null;
|
||||||
|
return e[1] !== e[2];
|
||||||
|
} else if (e[0] === ">") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2])) return e;
|
||||||
|
if (e[1] == null || e[2] == null) return null;
|
||||||
|
return e[1] > e[2];
|
||||||
|
} else if (e[0] === ">=") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2])) return e;
|
||||||
|
if (e[1] == null || e[2] == null) return null;
|
||||||
|
return e[1] >= e[2];
|
||||||
|
} else if (e[0] === "<") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2])) return e;
|
||||||
|
if (e[1] == null || e[2] == null) return null;
|
||||||
|
return e[1] < e[2];
|
||||||
|
} else if (e[0] === "<=") {
|
||||||
|
if (isArray(e[1]) || isArray(e[2])) return e;
|
||||||
|
if (e[1] == null || e[2] == null) return null;
|
||||||
|
return e[1] <= e[2];
|
||||||
|
} else if (e[0] === "*") {
|
||||||
|
return reduce(e, (a, b) => {
|
||||||
|
if (!isArray(a) && !isArray(b)) {
|
||||||
|
if (a == null || b == null) return null;
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
} else if (e[0] === "/") {
|
||||||
|
return reduce(e, (a, b, i) => {
|
||||||
|
if (!isArray(a) && !isArray(b)) {
|
||||||
|
if (a == null || b == null) return null;
|
||||||
|
return i === 0 ? a / b : a * b;
|
||||||
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
} else if (e[0] === "+") {
|
||||||
|
return reduce(e, (a, b) => {
|
||||||
|
if (!isArray(a) && !isArray(b)) {
|
||||||
|
if (a == null || b == null) return null;
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
} else if (e[0] === "-") {
|
||||||
|
return reduce(e, (a, b, i) => {
|
||||||
|
if (!isArray(a) && !isArray(b)) {
|
||||||
|
if (a == null || b == null) return null;
|
||||||
|
return i === 0 ? a - b : a + b;
|
||||||
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
} else if (e[0] === "||") {
|
||||||
|
return reduce(e, (a, b) => {
|
||||||
|
if (!isArray(a) && !isArray(b)) {
|
||||||
|
if (a == null || b == null) return null;
|
||||||
|
return `${a}${b}`;
|
||||||
|
}
|
||||||
|
return REDUCE_SKIP;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
export function evaluate(
|
export function evaluate(
|
||||||
exp,
|
exp,
|
||||||
obj,
|
obj,
|
||||||
@@ -63,14 +183,6 @@ export function evaluate(
|
|||||||
): string | number | boolean | null;
|
): string | number | boolean | null;
|
||||||
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression;
|
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression;
|
||||||
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
|
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
|
||||||
function getRegExp(pat, esc): RegExp {
|
|
||||||
const k = `${esc || ""}:${pat}`;
|
|
||||||
let c = regExpCache.get(exp);
|
|
||||||
if (!c) regExpCache.set(exp, (c = {}));
|
|
||||||
if (!c[k]) c[k] = likePatternToRegExp(pat, esc);
|
|
||||||
return c[k];
|
|
||||||
}
|
|
||||||
|
|
||||||
return map(exp, e => {
|
return map(exp, e => {
|
||||||
if (cb) e = cb(e);
|
if (cb) e = cb(e);
|
||||||
if (!isArray(e)) return e;
|
if (!isArray(e)) return e;
|
||||||
@@ -96,108 +208,53 @@ export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
|
|||||||
if (typeof v === "object") v = v.value ? v.value[0] : null;
|
if (typeof v === "object") v = v.value ? v.value[0] : null;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
} else if (e[0] === "AND") {
|
|
||||||
return reduce(e, (a, b) => {
|
|
||||||
if (!isArray(a)) return a ? b : a;
|
|
||||||
if (!isArray(b)) return b ? a : b;
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
} else if (e[0] === "OR") {
|
|
||||||
return reduce(e, (a, b) => {
|
|
||||||
if (!isArray(a)) return a ? a : b;
|
|
||||||
if (!isArray(b)) return b ? b : a;
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
} else if (e[0] === "NOT") {
|
|
||||||
if (!isArray(e[1])) return !e[1];
|
|
||||||
else if (e[1][0] === "NOT") return e[1][1];
|
|
||||||
} else if (e[0] === "IS NULL") {
|
|
||||||
if (isArray(e[1])) return e;
|
|
||||||
else if (e[1] == null) return true;
|
|
||||||
else return null;
|
|
||||||
} else if (e[0] === "IS NOT NULL") {
|
|
||||||
if (isArray(e[1])) return e;
|
|
||||||
else if (e[1] != null) return true;
|
|
||||||
else return null;
|
|
||||||
} else if (e[0] === "LIKE") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2]) || isArray(e[3])) return e;
|
|
||||||
else if (e[1] == null || e[2] == null || (e.length >= 4 && e[3] == null))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const r = getRegExp(e[2], e[3]);
|
|
||||||
return r.test(e[1]);
|
|
||||||
} else if (e[0] === "NOT LIKE") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2]) || isArray(e[3])) return e;
|
|
||||||
else if (e[1] == null || e[2] == null || (e.length >= 4 && e[3] == null))
|
|
||||||
return null;
|
|
||||||
const r = getRegExp(e[2], e[3]);
|
|
||||||
return !r.test(e[1]);
|
|
||||||
} else if (e[0] === "=") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2])) return e;
|
|
||||||
if (e[1] == null || e[2] == null) return null;
|
|
||||||
return e[1] === e[2];
|
|
||||||
} else if (e[0] === "<>") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2])) return e;
|
|
||||||
if (e[1] == null || e[2] == null) return null;
|
|
||||||
return e[1] !== e[2];
|
|
||||||
} else if (e[0] === ">") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2])) return e;
|
|
||||||
if (e[1] == null || e[2] == null) return null;
|
|
||||||
return e[1] > e[2];
|
|
||||||
} else if (e[0] === ">=") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2])) return e;
|
|
||||||
if (e[1] == null || e[2] == null) return null;
|
|
||||||
return e[1] >= e[2];
|
|
||||||
} else if (e[0] === "<") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2])) return e;
|
|
||||||
if (e[1] == null || e[2] == null) return null;
|
|
||||||
return e[1] < e[2];
|
|
||||||
} else if (e[0] === "<=") {
|
|
||||||
if (isArray(e[1]) || isArray(e[2])) return e;
|
|
||||||
if (e[1] == null || e[2] == null) return null;
|
|
||||||
return e[1] <= e[2];
|
|
||||||
} else if (e[0] === "*") {
|
|
||||||
return reduce(e, (a, b) => {
|
|
||||||
if (!isArray(a) && !isArray(b)) {
|
|
||||||
if (a == null || b == null) return null;
|
|
||||||
return a * b;
|
|
||||||
}
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
} else if (e[0] === "/") {
|
|
||||||
return reduce(e, (a, b, i) => {
|
|
||||||
if (!isArray(a) && !isArray(b)) {
|
|
||||||
if (a == null || b == null) return null;
|
|
||||||
return i === 0 ? a / b : a * b;
|
|
||||||
}
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
} else if (e[0] === "+") {
|
|
||||||
return reduce(e, (a, b) => {
|
|
||||||
if (!isArray(a) && !isArray(b)) {
|
|
||||||
if (a == null || b == null) return null;
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
} else if (e[0] === "-") {
|
|
||||||
return reduce(e, (a, b, i) => {
|
|
||||||
if (!isArray(a) && !isArray(b)) {
|
|
||||||
if (a == null || b == null) return null;
|
|
||||||
return i === 0 ? a - b : a + b;
|
|
||||||
}
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
} else if (e[0] === "||") {
|
|
||||||
return reduce(e, (a, b) => {
|
|
||||||
if (!isArray(a) && !isArray(b)) {
|
|
||||||
if (a == null || b == null) return null;
|
|
||||||
return `${a}${b}`;
|
|
||||||
}
|
|
||||||
return REDUCE_SKIP;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return e;
|
return evalExp(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function evaluateAsync(
|
||||||
|
exp,
|
||||||
|
obj,
|
||||||
|
now: number,
|
||||||
|
cb?: Function
|
||||||
|
): Promise<string | number | boolean | null>;
|
||||||
|
export async function evaluateAsync(
|
||||||
|
exp,
|
||||||
|
obj?,
|
||||||
|
now?: number,
|
||||||
|
cb?: Function
|
||||||
|
): Promise<Expression>;
|
||||||
|
export async function evaluateAsync(
|
||||||
|
exp,
|
||||||
|
obj?,
|
||||||
|
now?: number,
|
||||||
|
cb?: Function
|
||||||
|
): Promise<Expression> {
|
||||||
|
return mapAsync(exp, async e => {
|
||||||
|
if (cb) e = await cb(e);
|
||||||
|
if (!isArray(e)) return e;
|
||||||
|
|
||||||
|
if (e[0] === "FUNC") {
|
||||||
|
if (e[1] === "NOW") {
|
||||||
|
if (now) return now;
|
||||||
|
} else if (e[1] === "UPPER") {
|
||||||
|
if (e[2] == null) return null;
|
||||||
|
if (!isArray(e[2])) return `${e[2]}`.toUpperCase();
|
||||||
|
} else if (e[1] === "LOWER") {
|
||||||
|
if (e[2] == null) return null;
|
||||||
|
if (!isArray(e[2])) return `${e[2]}`.toLowerCase();
|
||||||
|
}
|
||||||
|
} else if (e[0] === "PARAM") {
|
||||||
|
if (e[1] == null) return null;
|
||||||
|
if (obj && !isArray(e[1])) {
|
||||||
|
let v = obj[e[1]];
|
||||||
|
if (v == null) return null;
|
||||||
|
if (typeof v === "object") v = v.value ? v.value[0] : null;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return evalExp(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user