Skip to content

Commit

Permalink
updated expression cache handling
Browse files Browse the repository at this point in the history
Signed-off-by: Tim Deubler <[email protected]>
  • Loading branch information
TerminalTim committed Jul 12, 2024
1 parent 83c1255 commit 014d50f
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 30 deletions.
18 changes: 14 additions & 4 deletions packages/common/src/Expressions/Expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,19 @@ export enum ExpressionMode {

export interface IExpression {
json: any[];

eval(context: any);
}

export type JSONExpression = [string, ...any[]];


let expId =0;
let expId = 0;

export abstract class Expression implements IExpression {
static operator: string;
id?: number;

static isExpression(exp) {
return exp instanceof Expression;
}
Expand All @@ -50,6 +53,7 @@ export abstract class Expression implements IExpression {
json: JSONExpression;

supportsPartialEval: boolean;

constructor(json: JSONExpression, env: ExpressionParser) {
// this.id = expId++;
this.json = json;
Expand Down Expand Up @@ -78,11 +82,17 @@ export abstract class Expression implements IExpression {
}

toJSON() {
return this.json.map((v)=>Expression.isExpression(v) ? v.toJSON(): v);
return this.json.map((v) => Expression.isExpression(v) ? v.toJSON() : v);
}

resolve(context=this.env.context, mode?: ExpressionMode) {
resolve(context?, mode?: ExpressionMode) {
const {env} = this;
return env.evaluate(this, context, mode);
let cache;
if (context === undefined) {
// resolve dynamic expression
context = this.env.context;
cache = env.dynamicResultCache;
}
return env.evaluate(this, context, mode, cache);
}
}
64 changes: 38 additions & 26 deletions packages/common/src/Expressions/ExpressionParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import {Expression, ExpressionMode, JSONExpression} from './Expression';
import * as Expressions from './Expressions';
import * as InterpolateExpressions from './InterpolateExpression';
import {StyleExpression} from '@here/xyz-maps-core';

type ResultCache = Map<Expression, any> & { hits?: number };

Expand All @@ -34,21 +35,27 @@ class DynamicExpressionInterrupt extends Error {
}
}

type Context = { [name: string]: any };
type Def = JSONExpression | Expression | boolean | number | string | null | any[];
type Value = {value:Def};
type Definitions = { [name: string]: Def | Value };


export class ExpressionParser {
static DYNAMIC_EXPRESSION_INTERRUPT: DynamicExpressionInterrupt = new DynamicExpressionInterrupt();
static Mode = ExpressionMode;
static Expressions: {
[op: string]: new (e: JSONExpression, p: ExpressionParser) => Expression & { [K in keyof typeof Expression]: typeof Expression[K] }
};

private definitions: {};
private definitions: Definitions;
private cache = new Map();
context: { [name: string]: any };
context: Context;
private _cacheHits: number = 0;
private defaultResultCache: ResultCache = new Map();
private resultCache: ResultCache;
private _mode: ExpressionMode;
private dynamicResultCache: ResultCache = new Map();
dynamicResultCache: ResultCache = new Map();

static {
let expressions = {};
Expand All @@ -58,26 +65,29 @@ export class ExpressionParser {
this.Expressions = expressions;
}

constructor(definitions = {}, context = {}) {
constructor(definitions: Definitions = {}, context: Context = {}) {
this.definitions = definitions;
this.context = context;
this.setMode(ExpressionMode.static);

this.defaultResultCache.hits = 0;
this.dynamicResultCache.hits = 0;

// console.time('clone definitions');
// this.definitions = JSUtils.clone(definitions);
// console.timeEnd('clone definitions');

// this.cache.get = this.cache.set =()=>undefined;
// this.defaultResultCache.get = this.defaultResultCache.set =()=>undefined;
// this.dynamicResultCache.get = this.dynamicResultCache.set =()=>undefined;
}

init(def, mapContext) {
init(def: Definitions, mapContext: Context) {
this.clearCache();
this.setDefinitions(def);
this.context = mapContext;
}

setDefinitions(def) {
setDefinitions(def: Definitions) {
this.definitions = def;
}

Expand All @@ -86,12 +96,12 @@ export class ExpressionParser {
this.cache.clear();
}

evaluate(exp, context, mode: ExpressionMode = ExpressionMode.static) {
evaluate(exp: Expression | JSONExpression, context: Context, mode: ExpressionMode = ExpressionMode.static, cache?) {
let result;
exp = this.parseJSON(exp);
try {
this.setMode(mode);
result = this.evaluateParsed(exp, context);
this.setMode(mode, cache);
result = this.evaluateParsed(exp as Expression, context);
} catch (e) {
if (e.message === 'DynamicExpressionInterrupt') {
return e.exp;
Expand All @@ -102,17 +112,18 @@ export class ExpressionParser {
return result;
}

evaluateParsed(exp: Expression, context) {
evaluateParsed(exp: Expression, context: Context) {
if (exp instanceof Expression) {
if (this.getMode() == ExpressionParser.Mode.dynamic && exp.dynamic() && !exp.supportsPartialEval) {
const DYNAMIC_EXPRESSION_INTERRUPT = ExpressionParser.DYNAMIC_EXPRESSION_INTERRUPT;
DYNAMIC_EXPRESSION_INTERRUPT.exp = exp;
throw DYNAMIC_EXPRESSION_INTERRUPT;
// return exp;
}
// return exp.eval(context);
let result = this.resultCache.get(exp);
if (result !== undefined) {
this.resultCache.hits = (this.resultCache.hits || 0) + 1;
this.resultCache.hits++;
return result;
}
result = exp.eval(context);
Expand All @@ -124,17 +135,17 @@ export class ExpressionParser {
}


resolveReference(exp: JSONExpression, definitions = this.definitions) {
resolveReference(exp: JSONExpression, definitions: Definitions = this.definitions) {
let key = exp?.[1];
let value = definitions[key];
if (value != null) {
if (value.value != null) {
value = value.value;
if ((value as Value).value != null) {
value = (value as Value).value;
}
while (ExpressionParser.isJSONExp(value) && value[0] == 'ref') {
value = definitions[value[1]];
if (value.value !== undefined) {
value = value.value;
if ((value as Value).value !== undefined) {
value = (value as Value).value;
}
}
}
Expand Down Expand Up @@ -183,7 +194,7 @@ export class ExpressionParser {
return Expression && new Expression(jsonExp, this);
}

static isJSONExp(exp) {
static isJSONExp(exp: any) {
return Array.isArray(exp) && typeof exp[0] == 'string';
}

Expand All @@ -195,19 +206,20 @@ export class ExpressionParser {
this.dynamicResultCache.clear();
}

isSupported(exp: JSONExpression) {
isSupported(exp: JSONExpression): boolean {
return Boolean(ExpressionParser.Expressions[exp[0]]);
}

setMode(mode: ExpressionMode) {
if (mode != this._mode) {
this._mode = mode;
this.context.mode = mode;
this.resultCache = mode === ExpressionMode.static ? this.defaultResultCache : this.dynamicResultCache;
}
setMode(mode: ExpressionMode, cache?: ResultCache) {
// if (mode != this._mode) {
this._mode = mode;
this.context.mode = mode;
this.resultCache = cache || this.defaultResultCache;
// this.resultCache = cache || (mode === ExpressionMode.static ? this.defaultResultCache : this.dynamicResultCache);
// }
}

getMode() {
getMode(): ExpressionMode {
return this._mode;
}
}

0 comments on commit 014d50f

Please sign in to comment.