2019-02-20 10:57:35 +00:00
|
|
|
|
// This should be the new way to implement mecanisms
|
|
|
|
|
// In a specific file
|
|
|
|
|
// TODO import them automatically
|
|
|
|
|
// TODO convert the legacy functions to new files
|
2020-03-03 14:46:06 +00:00
|
|
|
|
import Value from 'Components/Value'
|
2019-11-04 10:19:40 +00:00
|
|
|
|
import mecanismRound from 'Engine/mecanisms/arrondi'
|
2019-07-22 10:36:50 +00:00
|
|
|
|
import barème from 'Engine/mecanisms/barème'
|
2019-12-16 17:47:15 +00:00
|
|
|
|
import durée from 'Engine/mecanisms/durée'
|
2019-12-05 23:15:15 +00:00
|
|
|
|
import encadrement from 'Engine/mecanisms/encadrement'
|
2020-02-19 16:47:21 +00:00
|
|
|
|
import grille from 'Engine/mecanisms/grille'
|
2019-07-23 12:33:35 +00:00
|
|
|
|
import operation from 'Engine/mecanisms/operation'
|
2020-02-24 17:34:38 +00:00
|
|
|
|
import régularisation from 'Engine/mecanisms/régularisation'
|
2020-02-19 16:47:21 +00:00
|
|
|
|
import tauxProgressif from 'Engine/mecanisms/tauxProgressif'
|
2020-02-04 17:33:03 +00:00
|
|
|
|
import variableTemporelle from 'Engine/mecanisms/variableTemporelle'
|
2019-09-10 16:24:05 +00:00
|
|
|
|
import variations from 'Engine/mecanisms/variations'
|
|
|
|
|
import { Grammar, Parser } from 'nearley'
|
2019-04-03 15:40:51 +00:00
|
|
|
|
import {
|
|
|
|
|
add,
|
|
|
|
|
divide,
|
|
|
|
|
equals,
|
2019-09-10 16:24:05 +00:00
|
|
|
|
fromPairs,
|
2019-04-03 15:40:51 +00:00
|
|
|
|
gt,
|
|
|
|
|
gte,
|
|
|
|
|
lt,
|
|
|
|
|
lte,
|
|
|
|
|
multiply,
|
2020-02-19 16:47:21 +00:00
|
|
|
|
subtract
|
2019-04-03 15:40:51 +00:00
|
|
|
|
} from 'ramda'
|
|
|
|
|
import React from 'react'
|
2020-02-16 18:56:07 +00:00
|
|
|
|
import { EngineError, syntaxError } from './error'
|
2019-09-10 16:24:05 +00:00
|
|
|
|
import grammar from './grammar.ne'
|
2019-04-03 15:40:51 +00:00
|
|
|
|
import {
|
|
|
|
|
mecanismAllOf,
|
|
|
|
|
mecanismInversion,
|
|
|
|
|
mecanismMax,
|
|
|
|
|
mecanismMin,
|
|
|
|
|
mecanismOneOf,
|
2019-09-10 16:24:05 +00:00
|
|
|
|
mecanismOnePossibility,
|
2019-04-03 15:40:51 +00:00
|
|
|
|
mecanismProduct,
|
2020-01-02 14:39:31 +00:00
|
|
|
|
mecanismRecalcul,
|
2019-04-03 15:40:51 +00:00
|
|
|
|
mecanismReduction,
|
|
|
|
|
mecanismSum,
|
2019-09-10 16:24:05 +00:00
|
|
|
|
mecanismSynchronisation
|
2019-04-03 15:40:51 +00:00
|
|
|
|
} from './mecanisms'
|
2019-07-11 16:25:08 +00:00
|
|
|
|
import { parseReferenceTransforms } from './parseReference'
|
2019-06-13 16:01:49 +00:00
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
export const parse = (rules, rule, parsedRules) => rawNode => {
|
|
|
|
|
if (rawNode == null) {
|
|
|
|
|
syntaxError(
|
|
|
|
|
rule.dottedName,
|
|
|
|
|
`
|
|
|
|
|
Une des valeurs de la formule est vide.
|
|
|
|
|
Vérifiez que tous les champs à droite des deux points sont remplis`
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
if (typeof rawNode === 'boolean') {
|
|
|
|
|
syntaxError(
|
|
|
|
|
rule.dottedName,
|
|
|
|
|
`
|
|
|
|
|
Les valeure booléenes true / false ne sont acceptée.
|
|
|
|
|
Utilisez leur contrepartie française : 'oui' / 'non'`
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
const node =
|
|
|
|
|
typeof rawNode === 'object' ? rawNode : parseExpression(rule, '' + rawNode)
|
2019-06-13 16:01:49 +00:00
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const parsedNode = parseMecanism(rules, rule, parsedRules)(node)
|
|
|
|
|
parsedNode.evaluate = parsedNode.evaluate ?? ((_, __, ___, node) => node)
|
|
|
|
|
return parsedNode
|
2019-06-13 16:01:49 +00:00
|
|
|
|
}
|
2019-02-20 10:57:35 +00:00
|
|
|
|
|
2019-09-10 16:24:05 +00:00
|
|
|
|
const compiledGrammar = Grammar.fromCompiled(grammar)
|
2018-06-29 09:13:05 +00:00
|
|
|
|
|
2020-03-10 10:52:53 +00:00
|
|
|
|
export const parseExpression = (rule, rawNode) => {
|
2019-05-14 17:40:47 +00:00
|
|
|
|
/* Strings correspond to infix expressions.
|
|
|
|
|
* Indeed, a subset of expressions like simple arithmetic operations `3 + (quantity * 2)` or like `salary [month]` are more explicit that their prefixed counterparts.
|
|
|
|
|
* This function makes them prefixed operations. */
|
2019-11-26 18:14:57 +00:00
|
|
|
|
try {
|
|
|
|
|
let [parseResult] = new Parser(compiledGrammar).feed(rawNode).results
|
2020-02-19 16:47:21 +00:00
|
|
|
|
return parseResult
|
2019-11-26 18:14:57 +00:00
|
|
|
|
} catch (e) {
|
2019-11-28 11:03:23 +00:00
|
|
|
|
syntaxError(
|
|
|
|
|
rule.dottedName,
|
2020-02-19 16:47:21 +00:00
|
|
|
|
`\`${rawNode}\` n'est pas une expression valide`,
|
2019-11-28 11:03:23 +00:00
|
|
|
|
e
|
|
|
|
|
)
|
2019-11-26 18:14:57 +00:00
|
|
|
|
}
|
2018-06-29 09:13:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const parseMecanism = (rules, rule, parsedRules) => rawNode => {
|
2020-02-16 18:56:07 +00:00
|
|
|
|
if (Array.isArray(rawNode)) {
|
|
|
|
|
syntaxError(
|
|
|
|
|
rule.dottedName,
|
|
|
|
|
`
|
|
|
|
|
Il manque le nom du mécanisme pour le tableau : [${rawNode
|
|
|
|
|
.map(x => `'${x}'`)
|
|
|
|
|
.join(', ')}]
|
|
|
|
|
Les mécanisme possibles sont : 'somme', 'le maximum de', 'le minimum de', 'toutes ces conditions', 'une de ces conditions'.
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
}
|
2020-02-19 16:47:21 +00:00
|
|
|
|
if (Object.keys(rawNode).length > 1) {
|
|
|
|
|
syntaxError(
|
|
|
|
|
rule.dottedName,
|
|
|
|
|
`
|
|
|
|
|
Les mécanismes suivants se situent au même niveau : ${Object.keys(rawNode)
|
|
|
|
|
.map(x => `'${x}'`)
|
|
|
|
|
.join(', ')}
|
|
|
|
|
Cela vient probablement d'une erreur dans l'indentation
|
|
|
|
|
`
|
|
|
|
|
)
|
2019-09-10 16:24:05 +00:00
|
|
|
|
}
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const mecanismName = Object.keys(rawNode)[0]
|
|
|
|
|
const values = rawNode[mecanismName]
|
2019-05-15 08:52:20 +00:00
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const parseFunctions = {
|
|
|
|
|
...statelessParseFunction,
|
|
|
|
|
'une possibilité': mecanismOnePossibility(rule.dottedName),
|
|
|
|
|
'inversion numérique': mecanismInversion(rule.dottedName),
|
2020-01-02 14:39:31 +00:00
|
|
|
|
recalcul: mecanismRecalcul(rule.dottedName),
|
2020-02-19 16:47:21 +00:00
|
|
|
|
filter: () =>
|
|
|
|
|
parseReferenceTransforms(
|
|
|
|
|
rules,
|
|
|
|
|
rule,
|
|
|
|
|
parsedRules
|
|
|
|
|
)({
|
|
|
|
|
filter: values.filter,
|
|
|
|
|
variable: values.explanation
|
|
|
|
|
}),
|
|
|
|
|
variable: () =>
|
|
|
|
|
parseReferenceTransforms(rules, rule, parsedRules)({ variable: values }),
|
|
|
|
|
unitConversion: () =>
|
|
|
|
|
parseReferenceTransforms(
|
|
|
|
|
rules,
|
|
|
|
|
rule,
|
|
|
|
|
parsedRules
|
|
|
|
|
)({
|
|
|
|
|
variable: values.explanation,
|
|
|
|
|
unit: values.unit
|
|
|
|
|
})
|
|
|
|
|
}
|
2018-06-29 09:13:05 +00:00
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const parseFn = parseFunctions[mecanismName]
|
|
|
|
|
if (!parseFn) {
|
|
|
|
|
syntaxError(
|
|
|
|
|
rule.dottedName,
|
|
|
|
|
`
|
|
|
|
|
Le mécanisme ${mecanismName} est inconnu.
|
|
|
|
|
Vérifiez qu'il n'y ait pas d'erreur dans l'orthographe du nom.`
|
2019-05-15 08:52:20 +00:00
|
|
|
|
)
|
2020-02-19 16:47:21 +00:00
|
|
|
|
}
|
2020-02-16 18:56:07 +00:00
|
|
|
|
try {
|
|
|
|
|
return parseFn(parse(rules, rule, parsedRules), mecanismName, values)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (e instanceof EngineError) {
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
syntaxError(rule.dottedName, e.message)
|
|
|
|
|
}
|
2020-02-19 16:47:21 +00:00
|
|
|
|
}
|
2019-05-15 08:52:20 +00:00
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const knownOperations = {
|
|
|
|
|
'*': [multiply, '×'],
|
|
|
|
|
'/': [divide, '∕'],
|
|
|
|
|
'+': [add],
|
|
|
|
|
'-': [subtract, '−'],
|
|
|
|
|
'<': [lt],
|
|
|
|
|
'<=': [lte, '≤'],
|
|
|
|
|
'>': [gt],
|
|
|
|
|
'>=': [gte, '≥'],
|
|
|
|
|
'=': [equals],
|
|
|
|
|
'!=': [(a, b) => !equals(a, b), '≠']
|
|
|
|
|
}
|
2018-06-29 09:13:05 +00:00
|
|
|
|
|
2020-02-19 16:47:21 +00:00
|
|
|
|
const operationDispatch = fromPairs(
|
|
|
|
|
Object.entries(knownOperations).map(([k, [f, symbol]]) => [
|
|
|
|
|
k,
|
|
|
|
|
operation(k, f, symbol)
|
|
|
|
|
])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const statelessParseFunction = {
|
|
|
|
|
...operationDispatch,
|
|
|
|
|
'une de ces conditions': mecanismOneOf,
|
|
|
|
|
'toutes ces conditions': mecanismAllOf,
|
|
|
|
|
somme: mecanismSum,
|
2020-02-24 17:34:38 +00:00
|
|
|
|
régularisation,
|
2020-02-19 16:47:21 +00:00
|
|
|
|
multiplication: mecanismProduct,
|
2020-03-11 13:08:41 +00:00
|
|
|
|
produit: mecanismProduct,
|
2020-02-04 17:33:03 +00:00
|
|
|
|
temporalValue: variableTemporelle,
|
2020-02-19 16:47:21 +00:00
|
|
|
|
arrondi: mecanismRound,
|
|
|
|
|
barème,
|
|
|
|
|
grille,
|
|
|
|
|
'taux progressif': tauxProgressif,
|
|
|
|
|
encadrement,
|
|
|
|
|
durée,
|
|
|
|
|
'le maximum de': mecanismMax,
|
|
|
|
|
'le minimum de': mecanismMin,
|
|
|
|
|
allègement: mecanismReduction,
|
|
|
|
|
variations,
|
|
|
|
|
synchronisation: mecanismSynchronisation,
|
|
|
|
|
constant: (_, __, v) => ({
|
|
|
|
|
type: v.type,
|
|
|
|
|
nodeValue: v.nodeValue,
|
|
|
|
|
unit: v.unit,
|
|
|
|
|
// eslint-disable-next-line
|
|
|
|
|
jsx: (nodeValue, _, __, unit) => (
|
|
|
|
|
<span className={v.type}>
|
2020-03-03 14:46:06 +00:00
|
|
|
|
<Value
|
|
|
|
|
{...{
|
|
|
|
|
unit,
|
|
|
|
|
nodeValue,
|
|
|
|
|
// We want to display constants with full precision,
|
|
|
|
|
// espacilly for percentages like APEC 0,036 %
|
|
|
|
|
maximumFractionDigits: 5
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2020-02-19 16:47:21 +00:00
|
|
|
|
</span>
|
|
|
|
|
)
|
|
|
|
|
})
|
2018-06-29 09:13:05 +00:00
|
|
|
|
}
|