Implement evaluateAsync for expressions

This commit is contained in:
Zaid Abdulla committed 2019-06-05 00:17:36 -07:00
1 parent ae3779c484
commit 7e78336141
2 files changed
+121 -49

No files matched your search

+15
View File
@@ -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,
+106 -49
View File
@@ -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,48 +55,8 @@ export function likePatternToRegExp(pat, esc = "", flags = ""): RegExp {
return new RegExp(chars.join(""), flags); return new RegExp(chars.join(""), flags);
} }
export function evaluate( function evalExp(e: Expression): Expression {
exp, if (e[0] === "AND") {
obj,
now: number,
cb?: Function
): string | number | boolean | null;
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 => {
if (cb) e = 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;
if (typeof obj === "function") v = obj(e[1]);
else v = obj[e[1]];
if (v == null) return null;
if (typeof v === "object") v = v.value ? v.value[0] : null;
return v;
}
} else if (e[0] === "AND") {
return reduce(e, (a, b) => { return reduce(e, (a, b) => {
if (!isArray(a)) return a ? b : a; if (!isArray(a)) return a ? b : a;
if (!isArray(b)) return b ? a : b; if (!isArray(b)) return b ? a : b;
@@ -121,16 +81,31 @@ export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
else return null; else return null;
} else if (e[0] === "LIKE") { } else if (e[0] === "LIKE") {
if (isArray(e[1]) || isArray(e[2]) || isArray(e[3])) return e; 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)) else if (
e[1] == null ||
e[2] == null ||
((e as any[]).length >= 4 && e[3] == null)
)
return null; return null;
const r = regExpCache.get(e as any[]);
const r = getRegExp(e[2], e[3]); if (!r) {
likePatternToRegExp(e[2], e[3]);
regExpCache.set(e as any[], r);
}
return r.test(e[1]); return r.test(e[1]);
} else if (e[0] === "NOT LIKE") { } else if (e[0] === "NOT LIKE") {
if (isArray(e[1]) || isArray(e[2]) || isArray(e[3])) return e; 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)) else if (
e[1] == null ||
e[2] == null ||
((e as any[]).length >= 4 && e[3] == null)
)
return null; return null;
const r = getRegExp(e[2], e[3]); 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]); return !r.test(e[1]);
} else if (e[0] === "=") { } else if (e[0] === "=") {
if (isArray(e[1]) || isArray(e[2])) return e; if (isArray(e[1]) || isArray(e[2])) return e;
@@ -198,6 +173,88 @@ export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
}); });
} }
return e; return e;
}
export function evaluate(
exp,
obj,
now: number,
cb?: Function
): string | number | boolean | null;
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression;
export function evaluate(exp, obj?, now?: number, cb?: Function): Expression {
return map(exp, e => {
if (cb) e = 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;
if (typeof obj === "function") v = obj(e[1]);
else 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);
});
}
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);
}); });
} }