// 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 barème from 'Engine/mecanisms/barème' import barèmeContinu from 'Engine/mecanisms/barème-continu' import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire' import operation from 'Engine/mecanisms/operation' import variations from 'Engine/mecanisms/variations' import { Grammar, Parser } from 'nearley' import { add, cond, divide, equals, fromPairs, gt, gte, is, keys, lt, lte, multiply, propOr, subtract, T, without } from 'ramda' import React from 'react' import grammar from './grammar.ne' import { mecanismAllOf, mecanismComplement, mecanismError, mecanismInversion, mecanismMax, mecanismMin, mecanismNumericalSwitch, mecanismOneOf, mecanismOnePossibility, mecanismProduct, mecanismReduction, mecanismSum, mecanismSynchronisation } from './mecanisms' import { parseReferenceTransforms } from './parseReference' import { formatValue } from 'Engine/format' export let parse = (rules, rule, parsedRules) => rawNode => { let onNodeType = cond([ [is(String), parseString(rules, rule, parsedRules)], [is(Number), parseNumber], [is(Object), parseObject(rules, rule, parsedRules)], [T, parseOther] ]) let defaultEvaluate = (cache, situationGate, parsedRules, node) => node let parsedNode = onNodeType(rawNode) return parsedNode.evaluate ? parsedNode : { ...parsedNode, evaluate: defaultEvaluate } } const compiledGrammar = Grammar.fromCompiled(grammar) export let parseString = (rules, rule, parsedRules) => 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. */ let [parseResult] = new Parser(compiledGrammar).feed(rawNode).results return parseObject(rules, rule, parsedRules)(parseResult) } export let parseNumber = rawNode => ({ text: '' + rawNode, category: 'number', nodeValue: rawNode, type: 'numeric', jsx: {rawNode} }) export let parseOther = rawNode => { throw new Error( 'Cette donnée : ' + rawNode + ' doit être un Number, String ou Object' ) } export let parseObject = (rules, rule, parsedRules) => rawNode => { /* TODO instead of describing mecanisms in knownMecanisms.yaml, externalize the mecanisms themselves in an individual file and describe it let mecanisms = intersection(keys(rawNode), keys(knownMecanisms)) if (mecanisms.length != 1) { } */ let attributes = keys(rawNode), descriptiveAttributes = ['description', 'note', 'référence'], relevantAttributes = without(descriptiveAttributes, attributes) if (relevantAttributes.length !== 1) throw new Error(`OUPS : On ne devrait reconnaître que un et un seul mécanisme dans cet objet (au-delà des attributs descriptifs tels que "description", "commentaire", etc.) Objet YAML : ${JSON.stringify(rawNode)} Cette liste doit avoir un et un seul élément. Si vous venez tout juste d'ajouter un nouveau mécanisme, vérifier qu'il est bien intégré dans le dispatch de parse.js `) let k = relevantAttributes[0], v = rawNode[k] let knownOperations = { '*': [multiply, '×'], '/': [divide, '∕'], '+': [add], '-': [subtract, '−'], '<': [lt], '<=': [lte, '≤'], '>': [gt], '>=': [gte, '≥'], '=': [equals], '!=': [(a, b) => !equals(a, b), '≠'] }, operationDispatch = fromPairs( Object.entries(knownOperations).map(([k, [f, symbol]]) => [ k, operation(k, f, symbol) ]) ) let dispatch = { 'une de ces conditions': mecanismOneOf, 'toutes ces conditions': mecanismAllOf, 'aiguillage numérique': mecanismNumericalSwitch, somme: mecanismSum, multiplication: mecanismProduct, barème, 'barème linéaire': barèmeLinéaire, 'barème continu': barèmeContinu, 'le maximum de': mecanismMax, 'le minimum de': mecanismMin, complément: mecanismComplement, 'une possibilité': mecanismOnePossibility(rule.dottedName), 'inversion numérique': mecanismInversion(rule.dottedName), allègement: mecanismReduction, variations, synchronisation: mecanismSynchronisation, ...operationDispatch, filter: () => parseReferenceTransforms(rules, rule, parsedRules)({ filter: v.filter, variable: v.explanation }), variable: () => parseReferenceTransforms(rules, rule, parsedRules)({ variable: v }), temporalTransform: () => parseReferenceTransforms(rules, rule, parsedRules)({ variable: v.explanation, temporalTransform: v.temporalTransform }), constant: () => ({ type: v.type, nodeValue: v.nodeValue, unit: v.unit, // eslint-disable-next-line jsx: () => ( {formatValue({ unit: v.unit, value: v.nodeValue, // We want to display constants with full precision, // espacilly for percentages like APEC 0,036 % maximumFractionDigits: 5 })} ) }) }, action = propOr(mecanismError, k, dispatch) return action(parse(rules, rule, parsedRules), k, v) }