🔥 Supprime l'utilisation des temporals dans les mécanismes

pull/1514/head
Maxime Quandalle 2021-05-20 17:42:34 +02:00
parent f4e3b93af2
commit 49a7ae2af9
8 changed files with 108 additions and 308 deletions

View File

@ -7,17 +7,8 @@ import {
NodeKind,
} from './AST/types'
import { warning } from './error'
import { convertNodeToUnit, simplifyNodeUnit } from './nodeUnits'
import { convertNodeToUnit } from './nodeUnits'
import parse from './parse'
import {
concatTemporals,
liftTemporalNode,
mapTemporal,
pureTemporal,
Temporal,
temporalAverage,
zipTemporals,
} from './temporal'
export const collectNodeMissing = (
node: EvaluatedNode | ASTNode
@ -75,37 +66,17 @@ export const evaluateArray: <NodeName extends NodeKind>(
node.explanation.map(evaluate),
node.name
)
const values = evaluatedNodes.map(({ nodeValue }) => nodeValue)
const nodeValue = values.some((value) => value === null)
? null
: values.reduce(reducer, start)
const temporalValues = concatTemporals(
evaluatedNodes.map(
({ temporalValue, nodeValue }) =>
temporalValue ?? pureTemporal(nodeValue)
)
)
const temporalValue = mapTemporal((values) => {
if (values.some((value) => value === null)) {
return null
}
return values.reduce(reducer, start)
}, temporalValues)
const baseEvaluation = {
return {
...node,
missingVariables: mergeAllMissing(evaluatedNodes),
explanation: evaluatedNodes,
...(evaluatedNodes[0] && { unit: evaluatedNodes[0].unit }),
}
if (temporalValue.length === 1) {
return {
...baseEvaluation,
nodeValue: temporalValue[0].value,
}
}
return {
...baseEvaluation,
temporalValue,
nodeValue: temporalAverage(temporalValue as any),
nodeValue,
}
}
@ -132,71 +103,3 @@ export const parseObject = (objectShape, value, context) => {
})
)
}
export function evaluateObject<NodeName extends NodeKind>(
effet: (this: Engine, explanations: any) => any
) {
return function (node) {
const evaluations = Object.fromEntries(
Object.entries((node as any).explanation).map(([key, value]) => [
key,
this.evaluate(value as any),
])
)
const temporalExplanations = mapTemporal(
Object.fromEntries,
concatTemporals(
Object.entries(evaluations).map(([key, node]) =>
zipTemporals(pureTemporal(key), liftTemporalNode(node as ASTNode))
)
)
)
const temporalExplanation = mapTemporal((explanations) => {
const evaluation = effet.call(this, explanations)
return {
...evaluation,
explanation: {
...explanations,
...evaluation.explanation,
},
}
}, temporalExplanations)
const sameUnitTemporalExplanation: Temporal<
ASTNode & EvaluatedNode & { nodeValue: number }
> = convertNodesToSameUnit
.call(
this,
temporalExplanation.map((x) => x.value),
node.nodeKind
)
.map((node, i) => ({
...temporalExplanation[i],
value: simplifyNodeUnit(node),
}))
const temporalValue = mapTemporal(
({ nodeValue }) => nodeValue,
sameUnitTemporalExplanation
)
const nodeValue = temporalAverage(temporalValue)
const baseEvaluation = {
...node,
nodeValue,
unit: sameUnitTemporalExplanation[0].value.unit,
explanation: evaluations,
missingVariables: mergeAllMissing(Object.values(evaluations)),
}
if (sameUnitTemporalExplanation.length === 1) {
return {
...baseEvaluation,
explanation: (sameUnitTemporalExplanation[0] as any).value.explanation,
}
}
return {
...baseEvaluation,
temporalValue,
temporalExplanation,
}
} as EvaluationFunction<NodeName>
}

View File

@ -3,12 +3,6 @@ import { ASTNode } from '../AST/types'
import { defaultNode, mergeAllMissing } from '../evaluation'
import { registerEvaluationFunction } from '../evaluationFunctions'
import parse from '../parse'
import {
liftTemporal2,
liftTemporalNode,
mapTemporal,
temporalAverage,
} from '../temporal'
import { convertUnit, parseUnit } from '../units'
import {
evaluatePlafondUntilActiveTranche,
@ -78,44 +72,28 @@ const evaluate: EvaluationFunction<'barème'> = function (node) {
const evaluate = this.evaluate.bind(this)
const assiette = this.evaluate(node.explanation.assiette)
const multiplicateur = this.evaluate(node.explanation.multiplicateur)
const temporalTranchesPlafond = liftTemporal2(
(assiette, multiplicateur) =>
evaluatePlafondUntilActiveTranche.call(this, {
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur,
}),
liftTemporalNode(assiette as any),
liftTemporalNode(multiplicateur as any)
const tranches = evaluateBarème(
evaluatePlafondUntilActiveTranche.call(this, {
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur,
}),
assiette,
evaluate
)
const temporalTranches = liftTemporal2(
(tranches, assiette) => evaluateBarème(tranches, assiette, evaluate),
temporalTranchesPlafond,
liftTemporalNode(assiette as any)
)
const temporalValue = mapTemporal(
(tranches) =>
tranches.reduce(
(value, { nodeValue }) =>
nodeValue == null ? null : value + nodeValue,
0
),
temporalTranches
const nodeValue = tranches.reduce(
(value, { nodeValue }) => (nodeValue == null ? null : value + nodeValue),
0
)
return {
...node,
nodeValue: temporalAverage(temporalValue),
...(temporalValue.length > 1
? {
temporalValue,
}
: { missingVariables: mergeAllMissing(temporalTranches[0].value) }),
nodeValue,
missingVariables: mergeAllMissing(tranches),
explanation: {
assiette,
multiplicateur,
...(temporalTranches.length > 1
? { temporalTranches }
: { tranches: temporalTranches[0].value }),
tranches,
},
unit: assiette.unit,
} as any

View File

@ -3,12 +3,6 @@ import { ASTNode } from '../AST/types'
import { defaultNode, mergeAllMissing } from '../evaluation'
import { registerEvaluationFunction } from '../evaluationFunctions'
import parse from '../parse'
import {
liftTemporal2,
liftTemporalNode,
mapTemporal,
temporalAverage,
} from '../temporal'
import {
evaluatePlafondUntilActiveTranche,
parseTranches,
@ -37,78 +31,58 @@ export default function parseGrille(v, context): GrilleNode {
nodeKind: 'grille',
}
}
const evaluateGrille = (tranches, evaluate) =>
tranches.map((tranche) => {
if (tranche.isActive === false) {
return tranche
}
const montant = evaluate(tranche.montant)
return {
...tranche,
montant,
nodeValue: montant.nodeValue,
unit: montant.unit,
missingVariables: mergeAllMissing([montant, tranche]),
}
})
const evaluate: EvaluationFunction<'grille'> = function (node) {
const evaluate = this.evaluate.bind(this)
const assiette = this.evaluate(node.explanation.assiette)
const multiplicateur = this.evaluate(node.explanation.multiplicateur)
const temporalTranchesPlafond = liftTemporal2(
(assiette, multiplicateur) =>
evaluatePlafondUntilActiveTranche.call(this, {
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur,
}),
liftTemporalNode(assiette as any),
liftTemporalNode(multiplicateur as any)
)
const temporalTranches = mapTemporal(
(tranches) => evaluateGrille(tranches, evaluate),
temporalTranchesPlafond
)
const tranches = evaluatePlafondUntilActiveTranche
.call(this, {
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur,
})
.map((tranche) => {
if (tranche.isActive === false) {
return tranche
}
const montant = evaluate(tranche.montant)
return {
...tranche,
montant,
nodeValue: montant.nodeValue,
unit: montant.unit,
missingVariables: mergeAllMissing([montant, tranche]),
}
})
const activeTranches = mapTemporal((tranches) => {
const activeTranche = tranches.find((tranche) => tranche.isActive)
if (activeTranche) {
return [activeTranche]
}
const lastTranche = tranches[tranches.length - 1]
if (lastTranche.isAfterActive === false) {
return [{ nodeValue: false }]
}
return tranches.filter((tranche) => tranche.isActive === null)
}, temporalTranches)
const temporalValue = mapTemporal(
(tranches) =>
!tranches[0]
? false
: tranches[0].isActive === null
? null
: tranches[0].nodeValue,
activeTranches
)
let activeTranches
const activeTranche = tranches.find((tranche) => tranche.isActive)
if (activeTranche) {
activeTranches = [activeTranche]
} else if (tranches[tranches.length - 1].isAfterActive === false) {
activeTranches = [{ nodeValue: false }]
} else {
activeTranches = tranches.filter((tranche) => tranche.isActive === null)
}
const nodeValue = !activeTranches[0]
? false
: activeTranches[0].isActive === null
? null
: activeTranches[0].nodeValue
return {
...node,
nodeValue: temporalAverage(temporalValue),
...(temporalValue.length > 1
? {
temporalValue,
}
: { missingVariables: mergeAllMissing(activeTranches[0].value) }),
nodeValue,
missingVariables: mergeAllMissing(activeTranches),
explanation: {
...node.explanation,
assiette,
multiplicateur,
...(temporalTranches.length > 1
? { temporalTranches }
: { tranches: temporalTranches[0].value }),
tranches,
},
unit: activeTranches[0]?.value[0]?.unit ?? undefined,
unit: activeTranches[0]?.unit ?? undefined,
} as any
}

View File

@ -1,13 +1,11 @@
import { EvaluationFunction } from '..'
import { ASTNode } from '../AST/types'
import { ASTNode, EvaluatedNode } from '../AST/types'
import { convertToDate } from '../date'
import { warning } from '../error'
import { mergeAllMissing } from '../evaluation'
import { registerEvaluationFunction } from '../evaluationFunctions'
import { convertNodeToUnit } from '../nodeUnits'
import parse from '../parse'
import { liftTemporal2, pureTemporal, temporalAverage } from '../temporal'
import { EvaluatedNode } from '../AST/types'
import { inferUnit, serializeUnit } from '../units'
const knownOperations = {
@ -74,7 +72,26 @@ const evaluate: EvaluationFunction<'operation'> = function (node) {
)
}
}
const baseNode = {
const operatorFunction = knownOperations[node.operationKind][0]
const a = node1.nodeValue as string | false
const b = node2.nodeValue as string | false
const nodeValue =
!['≠', '='].includes(node.operator) && a === false && b === false
? false
: ['<', '>', '≤', '≥', '', '×'].includes(node.operator) &&
(a === false || b === false)
? false
: a !== false &&
b !== false &&
['≠', '=', '<', '>', '≤', '≥'].includes(node.operator) &&
[a, b].every((value) => value.match?.(/[\d]{2}\/[\d]{2}\/[\d]{4}/))
? operatorFunction(convertToDate(a), convertToDate(b))
: operatorFunction(a, b)
return {
...node,
explanation,
...((node.operationKind === '*' ||
@ -84,40 +101,7 @@ const evaluate: EvaluationFunction<'operation'> = function (node) {
unit: inferUnit(node.operationKind, [node1.unit, node2.unit]),
}),
missingVariables,
}
const operatorFunction = knownOperations[node.operationKind][0]
const temporalValue = liftTemporal2(
(a: string | false, b: string | false) => {
if (!['≠', '='].includes(node.operator) && a === false && b === false) {
return false
}
if (
['<', '>', '≤', '≥', '', '×'].includes(node.operator) &&
(a === false || b === false)
) {
return false
}
if (
a !== false &&
b !== false &&
['≠', '=', '<', '>', '≤', '≥'].includes(node.operator) &&
[a, b].every((value) => value.match?.(/[\d]{2}\/[\d]{2}\/[\d]{4}/))
) {
return operatorFunction(convertToDate(a), convertToDate(b))
}
return operatorFunction(a, b)
},
node1.temporalValue ?? (pureTemporal(node1.nodeValue) as any),
node2.temporalValue ?? (pureTemporal(node2.nodeValue) as any)
)
const nodeValue = temporalAverage(temporalValue, baseNode.unit)
return {
...baseNode,
nodeValue,
...(temporalValue.length > 1 && { temporalValue }),
}
}

View File

@ -1,7 +1,7 @@
import { EvaluationFunction } from '..'
import { ASTNode } from '../AST/types'
import { warning } from '../error'
import { defaultNode, evaluateObject, parseObject } from '../evaluation'
import { defaultNode, parseObject } from '../evaluation'
import { registerEvaluationFunction } from '../evaluationFunctions'
import { convertNodeToUnit, simplifyNodeUnit } from '../nodeUnits'
import { areUnitConvertible, convertUnit, inferUnit } from '../units'
@ -32,12 +32,12 @@ export const mecanismProduct = (v, context) => {
} as ProductNode
}
const productEffect: EvaluationFunction = function ({
assiette,
taux,
facteur,
plafond,
}: any) {
const evaluateProduit: EvaluationFunction<'produit'> = function (node) {
const assiette = this.evaluate(node.explanation.assiette)
const taux = this.evaluate(node.explanation.taux)
const facteur = this.evaluate(node.explanation.facteur)
let plafond = this.evaluate(node.explanation.plafond)
if (assiette.unit) {
try {
plafond = convertNodeToUnit(assiette.unit, plafond)
@ -77,11 +77,9 @@ const productEffect: EvaluationFunction = function ({
unit,
explanation: {
plafondActif: assiette.nodeValue > plafond.nodeValue,
plafondActif: (assiette.nodeValue as any) > (plafond as any).nodeValue,
},
})
}
const evaluate = evaluateObject<'produit'>(productEffect)
registerEvaluationFunction('produit', evaluate)
registerEvaluationFunction('produit', evaluateProduit)

View File

@ -62,9 +62,6 @@ const evaluateRecalcul: EvaluationFunction<'recalcul'> = function (node) {
},
missingVariables: evaluatedNode.missingVariables,
...('unit' in evaluatedNode && { unit: evaluatedNode.unit }),
...(evaluatedNode.temporalValue && {
temporalValue: evaluatedNode.temporalValue,
}),
}
}

View File

@ -1,17 +1,10 @@
import { EvaluationFunction } from '..'
import { ASTNode, Unit } from '../AST/types'
import { ASTNode, EvaluatedNode, Unit } from '../AST/types'
import { warning } from '../error'
import { bonus, defaultNode, mergeAllMissing } from '../evaluation'
import { registerEvaluationFunction } from '../evaluationFunctions'
import { convertNodeToUnit } from '../nodeUnits'
import parse from '../parse'
import {
liftTemporal2,
pureTemporal,
sometime,
Temporal,
temporalAverage,
} from '../temporal'
export type VariationNode = {
explanation: Array<{
@ -62,12 +55,12 @@ export default function parseVariations(v, context): VariationNode {
}
const evaluate: EvaluationFunction<'variations'> = function (node) {
const [temporalValue, explanation, unit] = node.explanation.reduce<
const [nodeValue, explanation, unit] = node.explanation.reduce<
[
Temporal<any>,
EvaluatedNode['nodeValue'],
VariationNode['explanation'],
Unit | undefined,
Temporal<any>
boolean | null
]
>(
(
@ -75,11 +68,7 @@ const evaluate: EvaluationFunction<'variations'> = function (node) {
{ condition, consequence },
i: number
) => {
const previousConditionsAlwaysTrue = !sometime(
(value) => value !== true,
previousConditions
)
if (previousConditionsAlwaysTrue) {
if (previousConditions === true) {
return [
evaluation,
[...explanations, { condition, consequence }],
@ -88,24 +77,19 @@ const evaluate: EvaluationFunction<'variations'> = function (node) {
]
}
const evaluatedCondition = this.evaluate(condition)
const currentCondition = liftTemporal2(
(previousCond, currentCond) =>
previousCond === null
? previousCond
: !previousCond &&
(currentCond === null ? null : currentCond !== false),
previousConditions,
evaluatedCondition.temporalValue ??
pureTemporal(evaluatedCondition.nodeValue)
)
const currentCondition =
previousConditions === null
? previousConditions
: !previousConditions &&
(evaluatedCondition.nodeValue === null
? null
: evaluatedCondition.nodeValue !== false)
evaluatedCondition.missingVariables = bonus(
evaluatedCondition.missingVariables
)
const currentConditionAlwaysFalse = !sometime(
(x) => x !== false,
currentCondition
)
if (currentConditionAlwaysFalse) {
if (currentCondition === false) {
return [
evaluation,
[...explanations, { condition: evaluatedCondition, consequence }],
@ -128,15 +112,8 @@ const evaluate: EvaluationFunction<'variations'> = function (node) {
)
}
}
const currentValue = liftTemporal2(
(cond, value) => cond && value,
currentCondition,
evaluatedConsequence.temporalValue ??
pureTemporal(evaluatedConsequence.nodeValue)
)
const or = (a, b) => a || b
return [
liftTemporal2(or, evaluation, currentValue),
currentCondition && evaluatedConsequence.nodeValue,
[
...explanations,
{
@ -146,13 +123,12 @@ const evaluate: EvaluationFunction<'variations'> = function (node) {
},
],
unit || evaluatedConsequence.unit,
liftTemporal2(or, previousConditions, currentCondition),
previousConditions || currentCondition,
]
},
[pureTemporal(false), [], undefined, pureTemporal(false)]
[false, [], undefined, false]
)
const nodeValue = temporalAverage(temporalValue, unit)
const missingVariables = mergeAllMissing(
explanation.reduce<ASTNode[]>(
(values, { condition, consequence }) => [
@ -170,7 +146,6 @@ const evaluate: EvaluationFunction<'variations'> = function (node) {
...(unit !== undefined && { unit }),
explanation,
missingVariables,
...(temporalValue.length > 1 && { temporalValue }),
}
}

View File

@ -1,5 +1,4 @@
import { EvaluatedNode, Unit } from './AST/types'
import { mapTemporal } from './temporal'
import { convertUnit, simplifyUnit } from './units'
export function simplifyNodeUnit(node) {
@ -15,20 +14,12 @@ export function convertNodeToUnit<Node extends EvaluatedNode = EvaluatedNode>(
to: Unit | undefined,
node: Node
): Node {
const temporalValue =
node.temporalValue && node.unit
? mapTemporal(
(value) => convertUnit(node.unit, to, value as number),
node.temporalValue
)
: node.temporalValue
return {
...node,
nodeValue:
node.unit && typeof node.nodeValue === 'number'
? convertUnit(node.unit, to, node.nodeValue)
: node.nodeValue,
...(temporalValue && { temporalValue }),
unit: to,
}
}