1
0
Fork 0
mirror of https://github.com/betagouv/mon-entreprise synced 2025-02-09 04:05:01 +00:00
mon-entreprise/source/engine/parse.js
Maxime Quandalle bfcf165c02
⚙️ Mécanisme recalcul
Utilisé pour le plafond des exonérations JEI et ZFU.

MAJ d'un test de non-regression car le plafonnement JEI n'était plus à
jour.
2020-02-25 19:03:26 +01:00

198 lines
5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
import { formatValue } from 'Engine/format'
import mecanismRound from 'Engine/mecanisms/arrondi'
import barème from 'Engine/mecanisms/barème'
import durée from 'Engine/mecanisms/durée'
import encadrement from 'Engine/mecanisms/encadrement'
import grille from 'Engine/mecanisms/grille'
import operation from 'Engine/mecanisms/operation'
import tauxProgressif from 'Engine/mecanisms/tauxProgressif'
import variations from 'Engine/mecanisms/variations'
import { Grammar, Parser } from 'nearley'
import {
add,
divide,
equals,
fromPairs,
gt,
gte,
lt,
lte,
multiply,
subtract
} from 'ramda'
import React from 'react'
import { syntaxError } from './error.ts'
import grammar from './grammar.ne'
import {
mecanismAllOf,
mecanismInversion,
mecanismMax,
mecanismMin,
mecanismOneOf,
mecanismOnePossibility,
mecanismProduct,
mecanismRecalcul,
mecanismReduction,
mecanismSum,
mecanismSynchronisation
} from './mecanisms'
import { parseReferenceTransforms } from './parseReference'
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)
const parsedNode = parseMecanism(rules, rule, parsedRules)(node)
parsedNode.evaluate = parsedNode.evaluate ?? ((_, __, ___, node) => node)
return parsedNode
}
const compiledGrammar = Grammar.fromCompiled(grammar)
const parseExpression = (rule, rawNode) => {
/* 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. */
try {
let [parseResult] = new Parser(compiledGrammar).feed(rawNode).results
return parseResult
} catch (e) {
syntaxError(
rule.dottedName,
`\`${rawNode}\` n'est pas une expression valide`,
e
)
}
}
const parseMecanism = (rules, rule, parsedRules) => rawNode => {
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
`
)
}
const mecanismName = Object.keys(rawNode)[0]
const values = rawNode[mecanismName]
const parseFunctions = {
...statelessParseFunction,
'une possibilité': mecanismOnePossibility(rule.dottedName),
'inversion numérique': mecanismInversion(rule.dottedName),
recalcul: mecanismRecalcul(rule.dottedName),
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
})
}
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.`
)
}
return parseFn(parse(rules, rule, parsedRules), mecanismName, values)
}
const knownOperations = {
'*': [multiply, '×'],
'/': [divide, ''],
'+': [add],
'-': [subtract, ''],
'<': [lt],
'<=': [lte, '≤'],
'>': [gt],
'>=': [gte, '≥'],
'=': [equals],
'!=': [(a, b) => !equals(a, b), '≠']
}
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,
multiplication: mecanismProduct,
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}>
{formatValue({
unit: unit,
value: nodeValue,
// TODO : handle localization here
language: 'fr',
// We want to display constants with full precision,
// espacilly for percentages like APEC 0,036 %
maximumFractionDigits: 5
})}
</span>
)
})
}