2019-09-10 14:58:26 +00:00
import { evaluateControls } from 'Engine/controls'
import parseRule from 'Engine/parseRule'
import { chain , path } from 'ramda'
import { evaluateNode } from './evaluation'
import { parseReference } from './parseReference'
2019-11-04 13:07:19 +00:00
import {
disambiguateRuleReference ,
findRule ,
findRuleByDottedName
} from './rules'
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-06-12 09:46:36 +00:00
/* First we parse each rule one by one. When a mechanism is encountered, it is 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
/ * 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 ` i n t h e d e f i n i t i o n o f ` A ` .
We need to map these exonerations to be able to retreive them from ` B ` * /
let nonApplicableMapping = { }
2019-10-30 15:52:51 +00:00
let replacedByMapping = { }
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 )
let targets = multiSimulation
? // 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
}
export let analyseMany = ( parsedRules , targetNames ) => situationGate => {
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
2018-04-26 12:24:47 +00:00
let cache = { parseLevel : 0 }
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 (
t =>
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 )
2019-04-09 15:07:17 +00:00
2019-03-04 13:21:15 +00:00
return { targets , cache , controls }
2017-11-28 11:45:06 +00:00
}
export let analyse = ( parsedRules , target ) => {
return analyseMany ( parsedRules , [ target ] )
2017-07-09 14:39:31 +00:00
}