2017-03-14 10:42:44 +00:00
import React from 'react'
2017-03-16 18:30:30 +00:00
import { findRuleByDottedName , completeRuleName , findRuleByName } from './rules'
import { evaluateVariable , knownVariable } from './variables'
2017-02-22 16:55:36 +00:00
import R from 'ramda'
2017-03-03 09:43:43 +00:00
import knownMecanisms from './known-mecanisms.yaml'
2017-03-07 17:25:25 +00:00
import { Parser } from 'nearley'
import Grammar from './grammar.ne'
2017-03-15 17:13:46 +00:00
import { Node , Leaf } from './traverse-common-jsx'
2017-03-14 10:42:44 +00:00
2017-03-07 17:25:25 +00:00
2017-03-08 16:47:12 +00:00
let nearley = ( ) => new Parser ( Grammar . ParserRules , Grammar . ParserStart )
2017-01-09 17:17:51 +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-07 19:10:04 +00:00
let transformPercentage = s =>
2017-03-01 10:21:53 +00:00
R . contains ( '%' ) ( s ) ?
+ s . replace ( '%' , '' ) / 100
: + s
2017-01-26 12:19:04 +00:00
2017-02-10 14:12:00 +00:00
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
- les feuilles de "l'une de ces conditions" sont courtcircuitées si l 'une d' elle est vraie
- 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-03-08 16:49:22 +00:00
let fillVariableNode = ( rule , situationGate ) => ( parseResult ) => {
let
{ fragments } = parseResult ,
variablePartialName = fragments . join ( ' . ' ) ,
2017-03-16 18:30:30 +00:00
variableName = completeRuleName ( rule , variablePartialName ) ,
// y = console.log('variableName', variableName),
variable = findRuleByDottedName ( variableName ) ,
variableIsRule = variable . formule != null ,
//TODO perf : mettre un cache sur les variables !
// On le fait pas pour l'instant car ça peut compliquer les fonctionnalités futures
// et qu'il n'y a aucun problème de perf aujourd'hui
parsedRule = variableIsRule && treatRuleRoot (
situationGate ,
variable
) ,
known = ! variableIsRule && knownVariable ( situationGate , variableName ) ,
nodeValue = variableIsRule ? parsedRule . nodeValue : ! known ? null : evaluateVariable ( situationGate , variableName )
2017-03-08 16:49:22 +00:00
return {
nodeValue ,
category : 'variable' ,
fragments : fragments ,
variableName ,
2017-03-27 13:02:50 +00:00
name : variableName ,
2017-03-08 16:49:22 +00:00
type : 'boolean | numeric' ,
2017-03-16 18:30:30 +00:00
explanation : parsedRule ,
missingVariables : ( variableIsRule || known ) ? [ ] : [ variableName ] ,
2017-03-15 17:13:46 +00:00
jsx : < Leaf
classes = "variable"
2017-03-20 09:00:05 +00:00
name = { fragments . join ( ' . ' ) }
2017-03-15 17:13:46 +00:00
value = { nodeValue }
/ >
2017-03-08 16:49:22 +00:00
}
}
2017-02-22 16:55:36 +00:00
let treat = ( situationGate , rule ) => rawNode => {
2017-03-09 15:42:52 +00:00
let reTreat = treat ( situationGate , rule )
2017-03-08 16:47:12 +00:00
if ( R . is ( String ) ( rawNode ) ) {
/ * O n a à f a i r e à u n s t r i n g , d o n c à u n e e x p r e s s i o n i n f i x e .
Elle sera traité avec le parser obtenu grâce ) NearleyJs et notre grammaire .
On obtient un objet de type Variable ( avec potentiellement un 'modifier' ) , CalcExpression ou Comparison .
Cet objet est alors rebalancé à 'treat' .
* /
2017-02-22 16:55:36 +00:00
2017-03-08 16:49:22 +00:00
let [ parseResult , ... additionnalResults ] = nearley ( ) . feed ( rawNode ) . results
2017-03-08 16:47:12 +00:00
if ( additionnalResults && additionnalResults . length > 0 ) throw "Attention ! L'expression <" + rawNode + '> ne peut être traitée de façon univoque'
2017-03-08 16:49:22 +00:00
if ( ! R . contains ( parseResult . category ) ( [ 'variable' , 'calcExpression' , 'modifiedVariable' , 'comparison' ] ) )
2017-03-08 16:47:12 +00:00
throw "Attention ! Erreur de traitement de l'expression : " + rawNode
2017-03-08 16:49:22 +00:00
if ( parseResult . category == 'variable' )
return fillVariableNode ( rule , situationGate ) ( parseResult , rawNode )
2017-03-09 15:42:52 +00:00
if ( parseResult . category == 'calcExpression' ) {
let
filledExplanation = parseResult . explanation . map (
2017-03-14 10:42:44 +00:00
R . cond ( [
[ R . propEq ( 'category' , 'variable' ) , fillVariableNode ( rule , situationGate ) ] ,
[ R . propEq ( 'category' , 'value' ) , node =>
R . assoc ( 'jsx' , < span className = "value" >
{ node . nodeValue }
< / s p a n > ) ( n o d e )
]
] )
2017-03-09 15:42:52 +00:00
) ,
[ { nodeValue : value1 } , { nodeValue : value2 } ] = filledExplanation ,
operatorFunctionName = {
'*' : 'multiply' ,
'/' : 'divide' ,
'+' : 'add' ,
'-' : 'subtract'
} [ parseResult . operator ] ,
operatorFunction = R [ operatorFunctionName ] ,
nodeValue = value1 == null || value2 == null ?
null
: operatorFunction ( value1 , value2 )
2017-03-08 16:47:12 +00:00
2017-03-09 15:42:52 +00:00
return {
text : rawNode ,
2017-03-14 10:42:44 +00:00
nodeValue ,
2017-03-09 15:42:52 +00:00
category : 'calcExpression' ,
type : 'numeric' ,
2017-03-14 10:42:44 +00:00
explanation : filledExplanation ,
2017-03-15 17:13:46 +00:00
jsx : < Node
classes = "inlineExpression calcExpression"
value = { nodeValue }
child = {
< span className = "nodeContent" >
{ filledExplanation [ 0 ] . jsx }
< span className = "operator" > { parseResult . operator } < / s p a n >
{ filledExplanation [ 1 ] . jsx }
< / s p a n >
}
/ >
2017-03-09 15:42:52 +00:00
}
}
if ( parseResult . category == 'comparison' ) {
//TODO mutualise code for 'comparison' & 'calclExpression'. Harmonise their names
2017-03-08 16:47:12 +00:00
let
2017-03-08 16:49:22 +00:00
filledExplanation = parseResult . explanation . map (
2017-03-14 10:42:44 +00:00
R . cond ( [
[ R . propEq ( 'category' , 'variable' ) , fillVariableNode ( rule , situationGate ) ] ,
[ R . propEq ( 'category' , 'value' ) , node =>
R . assoc ( 'jsx' , < span className = "value" >
{ node . nodeValue }
< / s p a n > ) ( n o d e )
]
] )
2017-03-08 16:49:22 +00:00
) ,
[ { nodeValue : value1 } , { nodeValue : value2 } ] = filledExplanation ,
comparatorFunctionName = {
'<' : 'lt' ,
'<=' : 'lte' ,
'>' : 'gt' ,
'>=' : 'gte'
//TODO '='
} [ parseResult . operator ] ,
comparatorFunction = R [ comparatorFunctionName ] ,
nodeValue = value1 == null || value2 == null ?
null
: comparatorFunction ( value1 , value2 )
2017-03-08 16:47:12 +00:00
return {
2017-03-08 16:49:22 +00:00
text : rawNode ,
nodeValue : nodeValue ,
category : 'comparison' ,
type : 'boolean' ,
2017-03-14 10:42:44 +00:00
explanation : filledExplanation ,
2017-03-15 17:13:46 +00:00
jsx : < Node
classes = "inlineExpression comparison"
value = { nodeValue }
child = {
< span className = "nodeContent" >
{ filledExplanation [ 0 ] . jsx }
< span className = "operator" > { parseResult . operator } < / s p a n >
{ filledExplanation [ 1 ] . jsx }
< / s p a n >
}
/ >
2017-03-08 16:47:12 +00:00
}
2017-02-22 16:55:36 +00:00
}
}
2017-03-03 09:43:43 +00:00
//TODO C'est pas bien ça. Devrait être traité par le parser plus haut !
if ( R . is ( Number ) ( rawNode ) ) {
return {
category : 'number' ,
nodeValue : rawNode ,
2017-03-14 10:42:44 +00:00
type : 'numeric' ,
jsx :
< span className = "number" >
{ rawNode }
< / s p a n >
2017-03-03 09:43:43 +00:00
}
}
2017-02-22 16:55:36 +00:00
2017-03-09 15:42:52 +00:00
2017-03-03 09:43:43 +00:00
if ( ! R . is ( Object ) ( rawNode ) ) {
2017-03-14 10:42:44 +00:00
console . log ( 'Cette donnée : ' , rawNode ) // eslint-disable-line no-console
2017-03-09 15:42:52 +00:00
throw ' doit être un Number, String ou Object'
2017-03-03 09:43:43 +00:00
}
2017-03-27 16:37:11 +00:00
let mecanisms = R . intersection ( R . keys ( rawNode ) , R . keys ( knownMecanisms ) )
2017-03-03 09:43:43 +00:00
if ( mecanisms . length != 1 ) throw 'OUPS !'
let k = R . head ( mecanisms ) ,
v = rawNode [ k ]
2017-02-22 16:55:36 +00:00
if ( k === "l'une de ces conditions" ) {
2017-03-14 10:42:44 +00:00
let result = R . pipe (
2017-02-22 16:55:36 +00:00
R . unless ( R . is ( Array ) , ( ) => { throw 'should be array' } ) ,
R . reduce ( ( memo , next ) => {
let { nodeValue , explanation } = memo ,
2017-03-09 15:42:52 +00:00
child = reTreat ( next ) ,
2017-02-22 16:55:36 +00:00
{ nodeValue : nextValue } = child
return { ... memo ,
// c'est un OU logique mais avec une préférence pour null sur false
nodeValue : nodeValue || nextValue || (
nodeValue == null ? null : nextValue
) ,
explanation : [ ... explanation , child ]
}
} , {
nodeValue : false ,
category : 'mecanism' ,
name : "l'une de ces conditions" ,
type : 'boolean' ,
explanation : [ ]
} ) // Reduce but don't use R.reduced to set the nodeValue : we need to treat all the nodes
) ( v )
2017-03-14 10:42:44 +00:00
return { ... result ,
2017-03-15 17:13:46 +00:00
jsx : < Node
classes = "mecanism list"
name = { result . name }
value = { result . nodeValue }
child = {
2017-03-14 10:42:44 +00:00
< ul >
2017-03-27 13:02:50 +00:00
{ result . explanation . map ( item => < li key = { item . name } > { item . jsx } < / l i > ) }
2017-03-15 17:13:46 +00:00
< / u l >
}
/ >
2017-03-14 10:42:44 +00:00
}
2017-02-22 16:55:36 +00:00
}
if ( k === 'toutes ces conditions' ) {
return R . pipe (
R . unless ( R . is ( Array ) , ( ) => { throw 'should be array' } ) ,
R . reduce ( ( memo , next ) => {
let { nodeValue , explanation } = memo ,
2017-03-09 15:42:52 +00:00
child = reTreat ( next ) ,
2017-02-22 16:55:36 +00:00
{ nodeValue : nextValue } = child
return { ... memo ,
// c'est un ET logique avec une possibilité de null
nodeValue : ! nodeValue ? nodeValue : nextValue ,
explanation : [ ... explanation , child ]
}
} , {
nodeValue : true ,
category : 'mecanism' ,
name : 'toutes ces conditions' ,
type : 'boolean' ,
explanation : [ ]
} ) // Reduce but don't use R.reduced to set the nodeValue : we need to treat all the nodes
) ( v )
}
2017-03-02 15:31:24 +00:00
//TODO perf: declare this closure somewhere else ?
let treatNumericalLogicRec =
R . ifElse (
R . is ( String ) ,
2017-03-14 10:42:44 +00:00
rate => ( { //TODO unifier ce code
2017-03-02 15:31:24 +00:00
nodeValue : transformPercentage ( rate ) ,
type : 'numeric' ,
category : 'percentage' ,
percentage : rate ,
2017-03-14 10:42:44 +00:00
explanation : null ,
jsx :
2017-03-15 17:13:46 +00:00
< span className = "percentage" >
2017-03-14 10:42:44 +00:00
< span className = "name" > { rate } < / s p a n >
< / s p a n >
2017-03-02 15:31:24 +00:00
} ) ,
R . pipe (
R . unless (
v => R . is ( Object ) ( v ) && R . keys ( v ) . length >= 1 ,
( ) => { throw 'Le mécanisme "logique numérique" et ses sous-logiques doivent contenir au moins une proposition' }
) ,
R . toPairs ,
R . reduce ( ( memo , [ condition , consequence ] ) => {
2017-03-08 16:49:22 +00:00
let
{ nodeValue , explanation } = memo ,
2017-03-09 15:42:52 +00:00
conditionNode = reTreat ( condition ) , // can be a 'comparison', a 'variable', TODO a 'negation'
2017-03-02 15:31:24 +00:00
childNumericalLogic = treatNumericalLogicRec ( consequence ) ,
2017-03-08 16:49:22 +00:00
nextNodeValue = conditionNode . nodeValue == null ?
2017-03-02 15:31:24 +00:00
// Si la proposition n'est pas encore résolvable
null
// Si la proposition est résolvable
2017-03-08 16:49:22 +00:00
: conditionNode . nodeValue == true ?
2017-03-02 15:31:24 +00:00
// Si elle est vraie
childNumericalLogic . nodeValue
// Si elle est fausse
: false
return { ... memo ,
nodeValue : nodeValue == null ?
null
: nodeValue !== false ?
nodeValue // l'une des propositions renvoie déjà une valeur numérique donc différente de false
: nextNodeValue ,
explanation : [ ... explanation , {
nodeValue : nextNodeValue ,
category : 'condition' ,
2017-03-08 16:49:22 +00:00
text : condition ,
condition : conditionNode ,
conditionValue : conditionNode . nodeValue ,
2017-03-02 15:31:24 +00:00
type : 'boolean' ,
2017-03-14 10:42:44 +00:00
explanation : childNumericalLogic ,
2017-03-15 17:13:46 +00:00
jsx : < div className = "condition" >
2017-03-14 10:42:44 +00:00
{ conditionNode . jsx }
2017-03-15 17:13:46 +00:00
< div >
{ childNumericalLogic . jsx }
< / d i v >
< / d i v >
2017-03-02 15:31:24 +00:00
} ] ,
}
} , {
nodeValue : false ,
category : 'mecanism' ,
name : "logique numérique" ,
type : 'boolean || numeric' , // lol !
explanation : [ ]
2017-03-14 10:42:44 +00:00
} ) ,
node => ( { ... node ,
2017-03-15 17:13:46 +00:00
jsx : < Node
classes = "mecanism numericalLogic list"
name = "logique numérique"
value = { node . nodeValue }
child = {
2017-03-14 10:42:44 +00:00
< ul >
2017-03-27 13:02:50 +00:00
{ node . explanation . map ( item => < li key = { item . name } > { item . jsx } < / l i > ) }
2017-03-15 17:13:46 +00:00
< / u l >
}
/ >
2017-03-02 15:31:24 +00:00
} )
) )
2017-03-03 09:43:43 +00:00
if ( k === 'logique numérique' ) {
2017-03-02 15:31:24 +00:00
return treatNumericalLogicRec ( v )
}
2017-03-03 09:43:43 +00:00
if ( k === 'taux' ) {
2017-03-02 15:31:24 +00:00
//TODO gérer les taux historisés
if ( R . is ( String ) ( v ) )
return {
category : 'percentage' ,
2017-03-14 10:42:44 +00:00
type : 'numeric' ,
2017-03-02 15:31:24 +00:00
percentage : v ,
nodeValue : transformPercentage ( v ) ,
2017-03-14 10:42:44 +00:00
explanation : null ,
jsx :
2017-03-15 17:13:46 +00:00
< span className = "percentage" >
2017-03-14 10:42:44 +00:00
< span className = "name" > { v } < / s p a n >
< / s p a n >
2017-03-02 15:31:24 +00:00
}
2017-04-06 15:15:35 +00:00
// Si c'est une liste historisée de pourcentages
// TODO revoir le test avant le bug de l'an 2100
else if ( R . all ( R . test ( /(19|20)\d\d(-\d\d)?(-\d\d)?/ ) ) ( R . keys ( v ) ) ) {
//TODO sélectionner la date de la simulation en cours
let lazySelection = R . first ( R . values ( v ) )
return {
category : 'percentage' ,
type : 'numeric' ,
percentage : lazySelection ,
nodeValue : transformPercentage ( lazySelection ) ,
explanation : null ,
jsx :
< span className = "percentage" >
< span className = "name" > { lazySelection } < / s p a n >
< / s p a n >
}
}
2017-03-02 15:31:24 +00:00
else {
2017-03-09 15:42:52 +00:00
let node = reTreat ( v )
2017-03-02 15:31:24 +00:00
return {
type : 'numeric' ,
category : 'percentage' ,
percentage : node . nodeValue ,
nodeValue : node . nodeValue ,
2017-03-14 10:42:44 +00:00
explanation : node ,
jsx : node . jsx
2017-03-02 15:31:24 +00:00
}
}
}
2017-03-16 18:30:30 +00:00
// Une simple somme de variables
if ( k === 'somme' ) {
let
summedVariables = v . map ( reTreat ) ,
nodeValue = summedVariables . reduce (
( memo , { nodeValue : nextNodeValue } ) => memo == null ? null : nextNodeValue == null ? null : memo + + nextNodeValue ,
0 )
return {
nodeValue ,
category : 'mecanism' ,
name : 'somme' ,
type : 'numeric' ,
explanation : summedVariables ,
jsx : < Node
classes = "mecanism somme"
name = "somme"
value = { nodeValue }
child = {
< ul >
{ summedVariables . map ( v => < li key = { v . name } > { v . jsx } < / l i > ) }
< / u l >
}
/ >
}
}
2017-03-01 10:21:53 +00:00
if ( k === 'multiplication' ) {
2017-03-08 16:49:22 +00:00
let
2017-03-09 15:42:52 +00:00
val = node => node . nodeValue ,
2017-04-05 14:20:58 +00:00
mult = ( base , rate , facteur , plafond ) =>
Math . min ( base , plafond ) * rate * facteur ,
constantNode = constant => ( { nodeValue : constant } ) ,
anyNull = R . any ( R . pipe ( val , R . equals ( null ) ) ) ,
assiette = reTreat ( v [ 'assiette' ] ) ,
//TODO parser le taux dans le parser ?
taux = v [ 'taux' ] ? reTreat ( { taux : v [ 'taux' ] } ) : constantNode ( 1 ) ,
facteur = v [ 'facteur' ] ? reTreat ( v [ 'facteur' ] ) : constantNode ( 1 ) ,
plafond = v [ 'plafond' ] ? reTreat ( v [ 'plafond' ] ) : constantNode ( Infinity ) ,
//TODO rate == false should be more explicit
nodeValue = ( val ( taux ) === 0 || val ( taux ) === false || val ( assiette ) === 0 || val ( facteur ) === 0 ) ?
2017-03-08 16:49:22 +00:00
0
2017-04-05 14:20:58 +00:00
: anyNull ( [ taux , assiette , facteur , plafond ] ) ?
2017-03-08 16:49:22 +00:00
null
2017-04-05 14:20:58 +00:00
: mult ( val ( assiette ) , val ( taux ) , val ( facteur ) , val ( plafond ) )
2017-03-15 17:13:46 +00:00
return {
nodeValue ,
2017-03-01 10:21:53 +00:00
category : 'mecanism' ,
name : 'multiplication' ,
type : 'numeric' ,
explanation : {
2017-04-05 14:20:58 +00:00
assiette ,
taux ,
facteur ,
plafond
2017-03-09 15:42:52 +00:00
//TODO introduire 'prorata' ou 'multiplicateur', pour sémantiser les opérandes ?
2017-03-14 10:42:44 +00:00
} ,
2017-03-15 17:13:46 +00:00
jsx : < Node
classes = "mecanism multiplication"
name = "multiplication"
value = { nodeValue }
child = {
< ul >
2017-04-05 14:20:58 +00:00
< li key = "assiette" >
2017-03-15 17:13:46 +00:00
< span className = "key" > assiette : < / s p a n >
2017-04-05 14:20:58 +00:00
< span className = "value" > { assiette . jsx } < / s p a n >
2017-03-15 17:13:46 +00:00
< / l i >
2017-04-05 14:20:58 +00:00
{ taux . nodeValue != 1 &&
< li key = "taux" >
2017-03-15 17:13:46 +00:00
< span className = "key" > taux : < / s p a n >
2017-04-05 14:20:58 +00:00
< span className = "value" > { taux . jsx } < / s p a n >
2017-03-15 17:13:46 +00:00
< / l i > }
{ facteur . nodeValue != 1 &&
2017-04-05 14:20:58 +00:00
< li key = "facteur" >
2017-03-15 17:13:46 +00:00
< span className = "key" > facteur : < / s p a n >
< span className = "value" > { facteur . jsx } < / s p a n >
< / l i > }
2017-04-05 14:20:58 +00:00
{ plafond . nodeValue != Infinity &&
< li key = "plafond" >
< span className = "key" > plafond : < / s p a n >
< span className = "value" > { plafond . jsx } < / s p a n >
< / l i > }
2017-03-15 17:13:46 +00:00
< / u l >
}
/ >
2017-03-01 10:21:53 +00:00
}
}
2017-03-03 09:43:43 +00:00
if ( k === 'le maximum de' ) {
let contenders = v . map ( treat ( situationGate , rule ) ) ,
contenderValues = R . pluck ( 'nodeValue' ) ( contenders ) ,
stopEverything = R . contains ( null , contenderValues ) ,
2017-03-14 10:42:44 +00:00
maxValue = R . max ( ... contenderValues ) ,
nodeValue = stopEverything ? null : maxValue
2017-03-03 09:43:43 +00:00
return {
type : 'numeric' ,
category : 'mecanism' ,
name : 'le maximum de' ,
2017-03-14 10:42:44 +00:00
nodeValue ,
explanation : contenders ,
2017-03-15 17:13:46 +00:00
jsx : < Node
classes = "mecanism list maximum"
name = "le maximum de"
value = { nodeValue }
child = {
2017-03-14 10:42:44 +00:00
< ul >
2017-03-15 17:13:46 +00:00
{ contenders . map ( ( item , i ) =>
2017-03-27 13:02:50 +00:00
< li key = { i } >
2017-03-15 17:13:46 +00:00
< div className = "description" > { v [ i ] . description } < / d i v >
{ item . jsx }
< / l i >
) }
< / u l >
}
/ >
2017-03-03 09:43:43 +00:00
}
}
throw "Le mécanisme qui vient d'être loggué est inconnu !"
2017-02-22 16:55:36 +00:00
}
2017-03-16 18:30:30 +00:00
let treatRuleRoot = ( situationGate , rule ) => R . pipe (
R . evolve ( { // -> Voilà les attributs que peut comporter, pour l'instant, une Variable.
2017-02-22 16:55:36 +00:00
// 'meta': pas de traitement pour l'instant
// 'cond' : Conditions d'applicabilité de la règle
2017-03-16 18:30:30 +00:00
'non applicable si' : value => {
let
child = treat ( situationGate , rule ) ( value ) ,
nodeValue = child . nodeValue
return {
category : 'ruleProp' ,
rulePropType : 'cond' ,
name : 'non applicable si' ,
type : 'boolean' ,
nodeValue : child . nodeValue ,
explanation : child ,
jsx : < Node
classes = "ruleProp mecanism cond"
name = "non applicable si"
value = { nodeValue }
child = {
child . jsx
}
/ >
}
2017-03-01 10:21:53 +00:00
}
2017-03-16 18:30:30 +00:00
,
// [n'importe quel mécanisme booléen] : expression booléenne (simple variable, négation, égalité, comparaison numérique, test d'inclusion court / long) || l'une de ces conditions || toutes ces conditions
// 'applicable si': // pareil mais inversé !
2017-02-22 16:55:36 +00:00
2017-03-16 18:30:30 +00:00
// note: pour certaines variables booléennes, ex. appartenance à régime Alsace-Moselle, la formule et le non applicable si se rejoignent
// [n'importe quel mécanisme numérique] : multiplication || barème en taux marginaux || le maximum de || le minimum de || ...
'formule' : value => {
let
child = treat ( situationGate , rule ) ( value ) ,
nodeValue = child . nodeValue
return {
category : 'ruleProp' ,
rulePropType : 'formula' ,
name : 'formule' ,
type : 'numeric' ,
nodeValue : nodeValue ,
explanation : child ,
shortCircuit : R . pathEq ( [ 'non applicable si' , 'nodeValue' ] , true ) ,
jsx : < Node
classes = "ruleProp mecanism formula"
name = "formule"
value = { nodeValue }
child = {
child . jsx
}
/ >
}
2017-03-01 10:21:53 +00:00
}
,
2017-02-22 16:55:36 +00:00
// TODO les mécanismes de composantes et de variations utilisables un peu partout !
// TODO 'temporal': information concernant les périodes : à définir !
// TODO 'intéractions': certaines variables vont en modifier d'autres : ex. Fillon va réduire voir annuler (set 0) une liste de cotisations
// ... ?
2017-03-16 18:30:30 +00:00
} ) ,
2017-03-20 11:17:49 +00:00
/ * C a l c u l d e l a v a l e u r d e l a v a r i a b l e e n c o m b i n a n t :
- les conditions d 'application (' non applicable si ' )
- la formule
TODO : mettre les conditions d ' application dans "formule" , et traiter la formule comme un mécanisme normal dans treat ( )
* /
2017-03-16 18:30:30 +00:00
r => {
let
formuleValue = r . formule . nodeValue ,
condValue = R . path ( [ 'non applicable si' , 'nodeValue' ] ) ( r ) ,
nodeValue =
condValue === undefined
? formuleValue
: formuleValue === 0
? 0
: condValue === null
? null
: condValue === true
? 0
: formuleValue
return { ... r , nodeValue }
}
) ( rule )
2017-02-22 16:55:36 +00:00
2017-02-10 14:12:00 +00:00
/ * A n a l y s e t h e s e t o f s e l e c t e d r u l e s , a n d a d d d e r i v e d i n f o r m a t i o n t o t h e m :
- do they need variables that are not present in the user situation ?
- if not , do they have a computed value or are they non applicable ?
* /
2017-03-27 13:32:03 +00:00
export let analyseSituation = situationGate =>
2017-03-27 13:02:50 +00:00
2017-03-16 18:30:30 +00:00
//TODO l'objectif devrait être spécifié par la page qui lance un simulateur
treatRuleRoot (
situationGate ,
findRuleByName ( 'surcoût CDD' )
2017-02-10 14:12:00 +00:00
)
2017-01-26 12:19:04 +00:00
2017-01-10 14:39:40 +00:00
export let variableType = name => {
2017-01-10 18:22:44 +00:00
if ( name == null ) return null
let found = findRuleByName ( name )
2017-01-10 14:39:40 +00:00
// tellement peu de variables pour l'instant
// que c'est très simpliste
2017-01-10 18:22:44 +00:00
if ( ! found ) return 'boolean'
2017-02-22 16:55:36 +00:00
let { rule } = found
2017-01-10 18:22:44 +00:00
if ( typeof rule . formule [ 'somme' ] !== 'undefined' ) return 'numeric'
2017-01-10 14:39:40 +00:00
}
2017-01-19 15:27:27 +00:00
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ce qui suit est la première tentative d ' écriture du principe du moteur et de la syntaxe * /
2017-01-09 17:17:51 +00:00
// let types = {
/ *
( expression ) :
| ( variable )
| ( négation )
| ( égalité )
| ( comparaison numérique )
| ( test d ' inclusion court )
* /
// }
/ *
Variable :
- applicable si : ( boolean logic )
- non applicable si : ( boolean logic )
- concerne : ( expression )
- ne concerne pas : ( expression )
( boolean logic ) :
toutes ces conditions : ( [ expression | boolean logic ] )
l ' une de ces conditions : ( [ expression | boolean logic ] )
conditions exclusives : ( [ expression | boolean logic ] )
"If you write a regular expression, walk away for a cup of coffee, come back, and can't easily understand what you just wrote, then you should look for a clearer way to express what you're doing."
Les expressions sont le seul mécanisme relativement embêtant pour le moteur . Dans un premier temps , il les gerera au moyen d 'expressions régulières, puis il faudra probablement mieux s' équiper avec un "javascript parser generator" :
https : //medium.com/@daffl/beyond-regex-writing-a-parser-in-javascript-8c9ed10576a6
( variable ) : ( string )
( négation ) :
! ( variable )
( égalité ) :
( variable ) = ( variable . type )
( comparaison numérique ) :
| ( variable ) < ( variable . type )
| ( variable ) <= ( variable . type )
| ( variable ) > ( variable . type )
| ( variable ) <= ( variable . type )
( test d ' inclusion court ) :
( variable ) ⊂ [ variable . type ]
in Variable . formule :
- composantes
- linéaire
- barème en taux marginaux
- test d 'inclusion: (test d' inclusion )
( test d ' inclusion ) :
variable : ( variable )
possibilités : [ variable . type ]
# pas nécessaire pour le CDD
in Variable
- variations : [ si ]
( si ) :
si : ( expression )
# corps
* /