87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
import { safeLoad } from 'js-yaml'
|
|
import { Simulation } from 'Reducers/rootReducer'
|
|
import { DottedName, Rule } from 'Types/rule'
|
|
import { evaluateNode } from './evaluation'
|
|
import { collectDefaults, enrichRule, rulesFr } from './rules'
|
|
import { parseAll } from './traverse'
|
|
import { parseUnit } from './units'
|
|
|
|
const emptyCache = {
|
|
_meta: { contextRule: [], defaultUnits: [] }
|
|
}
|
|
|
|
type EngineConfig = {
|
|
rules?: string | Array<any> | object
|
|
extra?: string | Array<any> | object
|
|
}
|
|
|
|
let enrichRules = input => {
|
|
const rules =
|
|
typeof input === 'string' ? safeLoad(input.replace(/\t/g, ' ')) : input
|
|
const rulesList = Array.isArray(rules)
|
|
? rules
|
|
: Object.entries(rules).map(([dottedName, rule]) => ({
|
|
dottedName,
|
|
...(rule as any)
|
|
}))
|
|
return rulesList.map(enrichRule)
|
|
}
|
|
|
|
export default class Engine {
|
|
rules: Array<Rule>
|
|
parsedRules: Record<DottedName, Rule>
|
|
defaultValues: Simulation['situation']
|
|
situation: Simulation['situation'] = {}
|
|
cache = { ...emptyCache }
|
|
|
|
constructor(config: EngineConfig = {}) {
|
|
this.rules = [
|
|
...(config?.rules ? enrichRules(config.rules) : rulesFr),
|
|
...(config?.extra ? enrichRules(config.extra) : [])
|
|
]
|
|
this.parsedRules = parseAll(this.rules) as any
|
|
this.defaultValues = collectDefaults(this.rules)
|
|
}
|
|
|
|
private resetCache() {
|
|
this.cache = { ...emptyCache }
|
|
}
|
|
|
|
setSituation(situation: Simulation['situation'] = {}) {
|
|
this.situation = situation
|
|
this.resetCache()
|
|
}
|
|
|
|
setDefaultUnits(defaultUnits = []) {
|
|
this.cache._meta.defaultUnits = defaultUnits.map(unit =>
|
|
parseUnit(unit)
|
|
) as any
|
|
}
|
|
|
|
evaluate(expression: string | Array<string>) {
|
|
const results = (Array.isArray(expression) ? expression : [expression]).map(
|
|
expr =>
|
|
this.cache[expr] ||
|
|
(this.parsedRules[expr]
|
|
? evaluateNode(
|
|
this.cache,
|
|
this.situationGate,
|
|
this.parsedRules,
|
|
this.parsedRules[expr]
|
|
// TODO: To support expressions (with operations, unit conversion,
|
|
// etc.) it should be enough to replace the above line with :
|
|
// parse(this.parsedRules, { dottedName: '' }, this.parsedRules)(expr)
|
|
// But currently there are small side effects (null values converted
|
|
// to 0), so we need to modify a little bit the engine before enabling
|
|
// publicode expressions in the UI.
|
|
)
|
|
: null)
|
|
)
|
|
|
|
return Array.isArray(expression) ? results : results[0]
|
|
}
|
|
|
|
situationGate = (dottedName: string) =>
|
|
this.situation[dottedName] || this.defaultValues[dottedName]
|
|
}
|