// 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.js'
import { Parser } from 'nearley'
import {
add,
curry,
divide,
equals,
gt,
gte,
keys,
without,
lt,
lte,
map,
multiply,
propOr,
subtract
} from 'ramda'
import React from 'react'
import { evaluateNode, makeJsx, mergeMissing, rewriteNode } from './evaluation'
import Grammar from './grammar.ne'
import {
mecanismAllOf,
mecanismComplement,
mecanismContinuousScale,
mecanismError,
mecanismInversion,
mecanismLinearScale,
mecanismMax,
mecanismMin,
mecanismNumericalSwitch,
mecanismOneOf,
mecanismProduct,
mecanismReduction,
mecanismSum,
mecanismSynchronisation,
mecanismVariations,
mecanismOnePossibility
} from './mecanisms'
import { Node } from './mecanismViews/common'
import { treat } from './traverse'
import {
treatNegatedVariable,
treatVariable,
treatVariableTransforms
} from './treatVariable'
export let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart)
export let treatString = (rules, 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. */
let [parseResult] = nearley().feed(rawNode).results
return treatObject(rules, rule)(parseResult)
}
export let treatNumber = rawNode => ({
text: '' + rawNode,
category: 'number',
nodeValue: rawNode,
type: 'numeric',
jsx: {rawNode}
})
export let treatOther = rawNode => {
throw new Error(
'Cette donnée : ' + rawNode + ' doit être un Number, String ou Object'
)
}
export let treatObject = (rules, rule, treatOptions) => 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 treat.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 = map(
([f, symbol]) => mecanismOperation(f, symbol || k),
knownOperations
)
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': mecanismLinearScale,
'barème continu': mecanismContinuousScale,
'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: mecanismVariations,
synchronisation: mecanismSynchronisation,
...operationDispatch,
'≠': () =>
treatNegatedVariable(treatVariable(rules, rule)(v.explanation)),
filter: () =>
treatVariableTransforms(rules, rule)({
filter: v.filter,
variable: v.explanation
}),
variable: () => treatVariableTransforms(rules, rule)({ variable: v }),
temporalTransform: () =>
treatVariableTransforms(rules, rule)({
variable: v.explanation,
temporalTransform: v.temporalTransform
}),
constant: () => ({
type: v.type,
nodeValue: v.nodeValue,
// eslint-disable-next-line
jsx: () => {v.rawNode}
})
},
action = propOr(mecanismError, k, dispatch)
return action(treat(rules, rule, treatOptions), k, v)
}
let mecanismOperation = (operatorFunction, symbol) => (recurse, k, v) => {
let evaluate = (cache, situation, parsedRules, node) => {
let explanation = map(
curry(evaluateNode)(cache, situation, parsedRules),
node.explanation
),
value1 = explanation[0].nodeValue,
value2 = explanation[1].nodeValue,
nodeValue =
value1 == null || value2 == null
? null
: operatorFunction(value1, value2),
missingVariables = mergeMissing(
explanation[0].missingVariables,
explanation[1].missingVariables
)
return rewriteNode(node, nodeValue, explanation, missingVariables)
}
let explanation = v.explanation.map(recurse)
let jsx = (nodeValue, explanation) => (
{makeJsx(explanation[0])}
{symbol}
{makeJsx(explanation[1])}
}
/>
)
return {
evaluate,
jsx,
operator: symbol,
// is this useful ? text: rawNode,
explanation
}
}