2019-09-10 14:58:26 +00:00
import { evaluateControls } from 'Engine/controls'
import parseRule from 'Engine/parseRule'
import { chain , path } from 'ramda'
2019-12-20 10:06:43 +00:00
import { DottedName , EvaluatedRule } from 'Types/rule'
2019-09-10 14:58:26 +00:00
import { evaluateNode } from './evaluation'
import { parseReference } from './parseReference'
2019-11-04 13:07:19 +00:00
import {
disambiguateRuleReference ,
findRule ,
findRuleByDottedName
} from './rules'
2019-12-13 16:22:18 +00:00
import { parseUnit , Unit } from './units'
2017-03-07 17:25:25 +00:00
2017-03-06 16:35:30 +00:00
/ *
Dans ce fichier , les r è gles YAML sont pars é es .
Elles expriment un langage orient é expression , les expressions é tant
- pr é fixes quand elles sont des 'mécanismes' ( des mot - clefs repr é sentant des calculs courants dans la loi )
- infixes pour les feuilles : des tests d 'égalité, d' inclusion , des comparaisons sur des variables ou tout simplement la variable elle - m ê me , ou une op é ration effectu é e sur la variable
* /
2017-02-22 16:55:36 +00:00
/ *
- > Notre r è gle est naturellement un AST ( car notation pr é fixe dans le YAML )
- > pr é liminaire : les expression infixes devront ê tre pars é es ,
par exemple ainsi : https : //github.com/Engelberg/instaparse#transforming-the-tree
- > Notre r è gle enti è re est un AST , qu ' il faut maintenant traiter :
- faire le calcul ( d é terminer les valeurs de chaque noeud )
- trouver les branches compl è tes pour d é terminer les autres branches courtcircuit é es
- ex . rule . formule est courtcircuit é e si rule . non applicable est vrai
2017-04-24 18:03:38 +00:00
- les feuilles de 'une de ces conditions' sont courtcircuit é es si l 'une d' elle est vraie
2017-02-22 16:55:36 +00:00
- les feuilles de "toutes ces conditions" sont courtcircuit é es si l 'une d' elle est fausse
- . . .
( - bonus : utiliser ces informations pour l ' ordre de priorit é des variables inconnues )
- si une branche est incompl è te et qu 'elle est de type numérique, déterminer les bornes si c' est possible .
Ex . - pour une multiplication , si l 'assiette est connue mais que l ' applicabilit é est inconnue ,
les bornes seront [ 0 , multiplication . value = assiette * taux ]
- si taux = effectif entreprise >= 20 ? 1 % : 2 % et que l ' applicabilit é est connue ,
bornes = [ assiette * 1 % , assiette * 2 % ]
- transformer l ' arbre en JSX pour afficher le calcul * et son é tat en prenant en compte les variables renseign é es et calcul é es * de fa ç on sympathique dans un butineur Web tel que Mozilla Firefox .
- surement plein d ' autres applications . . .
* /
2017-11-28 11:45:06 +00:00
export let parseAll = flatRules = > {
2019-12-13 16:22:18 +00:00
/ * F i r s t w e p a r s e e a c h r u l e o n e b y o n e . W h e n a m e c h a n i s m i s e n c o u n t e r e d , i t i s
recursively parsed . When a reference to a variable is encountered , a
'variable' node is created , we don ' t parse variables recursively . * /
2019-06-13 16:17:22 +00:00
let parsedRules = { }
2019-08-19 07:36:27 +00:00
2019-12-13 16:22:18 +00:00
/ * A r u l e ` A ` c a n d i s a b l e a r u l e ` B ` u s i n g t h e r u l e ` r e n d n o n a p p l i c a b l e : B `
in the definition of ` A ` . We need to map these exonerations to be able to
retreive them from ` B ` * /
let nonApplicableMapping : Record < string , any > = { }
let replacedByMapping : Record < string , any > = { }
2019-07-29 07:13:05 +00:00
flatRules . forEach ( rule = > {
const parsed = parseRule ( flatRules , rule , parsedRules )
2019-08-19 07:36:27 +00:00
if ( parsed [ 'rend non applicable' ] ) {
nonApplicableMapping [ rule . dottedName ] = parsed [ 'rend non applicable' ]
2019-07-29 07:13:05 +00:00
}
2019-10-30 15:52:51 +00:00
2019-11-04 15:09:09 +00:00
const replaceDescriptors = parsed [ 'remplace' ]
if ( replaceDescriptors ) {
replaceDescriptors . forEach (
descriptor = >
( replacedByMapping [ descriptor . referenceName ] = [
. . . ( replacedByMapping [ descriptor . referenceName ] ? ? [ ] ) ,
{ . . . descriptor , referenceName : rule.dottedName }
] )
)
2019-10-30 15:52:51 +00:00
}
2019-07-29 07:13:05 +00:00
} )
2019-08-19 07:36:27 +00:00
Object . entries ( nonApplicableMapping ) . forEach ( ( [ a , b ] ) = > {
2019-07-29 07:13:05 +00:00
b . forEach ( ruleName = > {
parsedRules [ ruleName ] . isDisabledBy . push (
2019-11-04 13:07:19 +00:00
parseReference ( flatRules , parsedRules [ ruleName ] , parsedRules ) ( a )
2019-10-30 15:52:51 +00:00
)
} )
} )
Object . entries ( replacedByMapping ) . forEach ( ( [ a , b ] ) = > {
2019-11-04 13:07:19 +00:00
parsedRules [ a ] . replacedBy = b . map ( ( { referenceName , . . . other } ) = > ( {
referenceNode : parseReference (
flatRules ,
parsedRules [ referenceName ] ,
parsedRules
) ( referenceName ) ,
. . . other
} ) )
2019-07-29 07:13:05 +00:00
} )
2019-10-30 15:52:51 +00:00
2019-09-10 14:58:26 +00:00
/ * T h e n w e n e e d t o i n f e r u n i t s . S i n c e o n l y r e f e r e n c e s t o v a r i a b l e s h a v e b e e n c r e a t e d , w e n e e d t o w a i t f o r t h e l a t t e r m a p t o c o m p l e t e b e f o r e s t a r t i n g t h i s j o b . C o n s i d e r t h i s e x a m p l e :
2019-06-12 09:46:36 +00:00
A = B * C
B = D / E
2019-10-30 15:52:51 +00:00
2019-06-12 09:46:36 +00:00
C unit é km
D unit é €
E unit é km
*
* When parsing A 's formula, we don' t know the unit of B , since only the final nodes have units ( it would be too cumbersome to specify a unit to each variable ) , and B hasn ' t been parsed yet .
*
* * /
2019-06-13 16:17:22 +00:00
return parsedRules
2019-06-13 16:01:49 +00:00
}
2019-06-12 09:46:36 +00:00
2019-06-13 16:01:49 +00:00
export let getTargets = ( target , rules ) = > {
let multiSimulation = path ( [ 'simulateur' , 'objectifs' ] ) ( target )
2019-12-13 16:22:18 +00:00
let targets = Array . isArray ( multiSimulation )
2019-06-13 16:01:49 +00:00
? // On a un simulateur qui définit une liste d'objectifs
2019-11-04 13:07:19 +00:00
multiSimulation
. map ( n = > disambiguateRuleReference ( rules , target , n ) )
. map ( n = > findRuleByDottedName ( rules , n ) )
2019-06-13 16:01:49 +00:00
: // Sinon on est dans le cas d'une simple variable d'objectif
2019-11-04 13:07:19 +00:00
[ target ]
2019-06-12 09:46:36 +00:00
2019-06-13 16:01:49 +00:00
return targets
2017-11-28 11:45:06 +00:00
}
2019-12-13 16:22:18 +00:00
type CacheMeta = {
contextRule : Array < string >
defaultUnits : Array < Unit >
inversionFail ? : {
given : string
estimated : string
}
}
2019-12-18 16:47:43 +00:00
export let analyseMany = (
parsedRules ,
targetNames ,
defaultUnits : Array < string > = [ ]
) = > ( situationGate : ( name : DottedName ) = > any ) = > {
2018-01-03 15:54:19 +00:00
// TODO: we should really make use of namespaces at this level, in particular
// setRule in Rule.js needs to get smarter and pass dottedName
2019-12-18 16:47:43 +00:00
const defaultParsedUnits = defaultUnits . map ( parseUnit )
2019-11-28 11:03:23 +00:00
let cache = {
2019-12-18 16:47:43 +00:00
_meta : { contextRule : [ ] , defaultUnits : defaultParsedUnits } as CacheMeta
2019-11-28 11:03:23 +00:00
}
2018-06-29 13:46:42 +00:00
2018-09-26 13:00:05 +00:00
let parsedTargets = targetNames . map ( t = > {
2019-11-04 13:07:19 +00:00
let parsedTarget = findRule ( parsedRules , t )
if ( ! parsedTarget )
throw new Error (
` L'objectif de calcul " ${ t } " ne semble pas exister dans la base de règles `
)
return parsedTarget
} ) ,
2018-11-15 15:21:53 +00:00
targets = chain ( pt = > getTargets ( pt , parsedRules ) , parsedTargets ) . map (
2019-12-20 10:06:43 +00:00
( t ) : EvaluatedRule = >
2019-06-13 16:01:49 +00:00
cache [ t . dottedName ] || // This check exists because it is not done in parseRuleRoot's eval, while it is in parseVariable. This should be merged : we should probably call parseVariable here : targetNames could be expressions (hence with filters) TODO
2018-11-15 15:21:53 +00:00
evaluateNode ( cache , situationGate , parsedRules , t )
2017-11-07 18:46:40 +00:00
)
2019-03-04 13:21:15 +00:00
let controls = evaluateControls ( cache , situationGate , parsedRules )
return { targets , cache , controls }
2017-11-28 11:45:06 +00:00
}
2019-12-13 16:22:18 +00:00
export type Analysis = ReturnType < ReturnType < typeof analyse > >
2019-11-28 11:03:23 +00:00
export let analyse = ( parsedRules , target , defaultUnits = [ ] ) = > {
return analyseMany ( parsedRules , [ target ] , defaultUnits )
2017-07-09 14:39:31 +00:00
}