2018-06-29 09:13:05 +00:00
|
|
|
|
import React from 'react'
|
|
|
|
|
import { Parser } from 'nearley'
|
|
|
|
|
import Grammar from './grammar.ne'
|
|
|
|
|
import {
|
|
|
|
|
contains,
|
|
|
|
|
propEq,
|
|
|
|
|
curry,
|
|
|
|
|
cond,
|
|
|
|
|
equals,
|
|
|
|
|
divide,
|
|
|
|
|
multiply,
|
|
|
|
|
map,
|
|
|
|
|
intersection,
|
|
|
|
|
keys,
|
|
|
|
|
propOr,
|
|
|
|
|
always,
|
|
|
|
|
head,
|
|
|
|
|
gte,
|
|
|
|
|
lte,
|
|
|
|
|
lt,
|
|
|
|
|
gt,
|
|
|
|
|
add,
|
|
|
|
|
subtract
|
|
|
|
|
} from 'ramda'
|
|
|
|
|
import { evaluateNode, rewriteNode, makeJsx, mergeMissing } from './evaluation'
|
|
|
|
|
import { Node } from './mecanismViews/common'
|
|
|
|
|
import {
|
|
|
|
|
treatVariable,
|
|
|
|
|
treatNegatedVariable,
|
2018-10-01 15:41:55 +00:00
|
|
|
|
treatVariableTransforms
|
2018-06-29 09:13:05 +00:00
|
|
|
|
} from './treatVariable'
|
|
|
|
|
import { treat } from './traverse'
|
|
|
|
|
import knownMecanisms from './known-mecanisms.yaml'
|
|
|
|
|
import {
|
|
|
|
|
mecanismOneOf,
|
|
|
|
|
mecanismAllOf,
|
|
|
|
|
mecanismNumericalSwitch,
|
|
|
|
|
mecanismSum,
|
|
|
|
|
mecanismProduct,
|
|
|
|
|
mecanismScale,
|
2018-07-09 12:13:38 +00:00
|
|
|
|
mecanismLinearScale,
|
2018-12-05 17:19:11 +00:00
|
|
|
|
mecanismContinuousScale,
|
2018-06-29 09:13:05 +00:00
|
|
|
|
mecanismMax,
|
|
|
|
|
mecanismMin,
|
|
|
|
|
mecanismError,
|
|
|
|
|
mecanismComplement,
|
|
|
|
|
mecanismSelection,
|
|
|
|
|
mecanismInversion,
|
2018-08-07 16:20:08 +00:00
|
|
|
|
mecanismReduction,
|
2018-09-06 14:45:50 +00:00
|
|
|
|
mecanismVariations,
|
|
|
|
|
mecanismSynchronisation
|
2018-06-29 09:13:05 +00:00
|
|
|
|
} from './mecanisms'
|
|
|
|
|
|
|
|
|
|
let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart)
|
|
|
|
|
|
|
|
|
|
export let treatString = (rules, rule) => rawNode => {
|
|
|
|
|
/* On a affaire à un string, donc à une expression infixe.
|
|
|
|
|
Elle sera traité avec le parser obtenu grâce à NearleyJs et notre grammaire `grammar.ne`.
|
2018-11-13 15:14:57 +00:00
|
|
|
|
On obtient un objet de type Variable (avec potentiellement un 'modifier', par exemple temporel), CalcExpression ou Comparison.
|
2018-06-29 09:13:05 +00:00
|
|
|
|
Cet objet est alors rebalancé à 'treat'.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
let [parseResult, ...additionnalResults] = nearley().feed(rawNode).results
|
|
|
|
|
|
2018-08-07 18:46:26 +00:00
|
|
|
|
if (
|
|
|
|
|
additionnalResults &&
|
|
|
|
|
additionnalResults.length > 0 &&
|
|
|
|
|
parseResult.category !== 'boolean'
|
|
|
|
|
) {
|
|
|
|
|
// booleans, 'oui' and 'non', have an exceptional resolving precedence
|
|
|
|
|
throw new Error(
|
|
|
|
|
"Attention ! L'expression <" +
|
|
|
|
|
rawNode +
|
|
|
|
|
'> ne peut être traitée de façon univoque'
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-06-29 09:13:05 +00:00
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!contains(parseResult.category)([
|
|
|
|
|
'variable',
|
|
|
|
|
'calcExpression',
|
|
|
|
|
'filteredVariable',
|
|
|
|
|
'comparison',
|
|
|
|
|
'negatedVariable',
|
2018-08-07 18:46:26 +00:00
|
|
|
|
'percentage',
|
|
|
|
|
'boolean'
|
2018-06-29 09:13:05 +00:00
|
|
|
|
])
|
|
|
|
|
)
|
2018-08-07 18:46:26 +00:00
|
|
|
|
throw new Error(
|
|
|
|
|
"Attention ! Erreur de traitement de l'expression : " + rawNode
|
|
|
|
|
)
|
2018-06-29 09:13:05 +00:00
|
|
|
|
|
|
|
|
|
if (parseResult.category == 'variable')
|
2018-10-01 15:41:55 +00:00
|
|
|
|
return treatVariableTransforms(rules, rule)(parseResult)
|
2018-06-29 09:13:05 +00:00
|
|
|
|
if (parseResult.category == 'negatedVariable')
|
|
|
|
|
return treatNegatedVariable(
|
|
|
|
|
treatVariable(rules, rule)(parseResult.variable)
|
|
|
|
|
)
|
|
|
|
|
|
2018-08-07 18:46:26 +00:00
|
|
|
|
if (parseResult.category == 'boolean') {
|
|
|
|
|
return {
|
|
|
|
|
nodeValue: parseResult.nodeValue,
|
|
|
|
|
// eslint-disable-next-line
|
|
|
|
|
jsx: () => <span className="boolean">{rawNode}</span>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-29 09:13:05 +00:00
|
|
|
|
// We don't need to handle category == 'value' because YAML then returns it as
|
|
|
|
|
// numerical value, not a String: it goes to treatNumber
|
2018-07-30 13:30:54 +00:00
|
|
|
|
if (parseResult.category == 'percentage')
|
|
|
|
|
return {
|
|
|
|
|
nodeValue: parseResult.nodeValue,
|
|
|
|
|
category: 'percentage',
|
|
|
|
|
// eslint-disable-next-line
|
|
|
|
|
jsx: () => <span className="value">{rawNode.split('%')[0]} %</span>
|
|
|
|
|
//on ajoute l'espace nécessaire en français avant le pourcentage
|
|
|
|
|
}
|
2018-06-29 09:13:05 +00:00
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
parseResult.category == 'calcExpression' ||
|
|
|
|
|
parseResult.category == 'comparison'
|
|
|
|
|
) {
|
|
|
|
|
let evaluate = (cache, situation, parsedRules, node) => {
|
|
|
|
|
let operatorFunction = {
|
|
|
|
|
'*': multiply,
|
|
|
|
|
'/': divide,
|
|
|
|
|
'+': add,
|
|
|
|
|
'-': subtract,
|
|
|
|
|
'<': lt,
|
|
|
|
|
'<=': lte,
|
|
|
|
|
'>': gt,
|
|
|
|
|
'>=': gte,
|
|
|
|
|
'=': equals,
|
|
|
|
|
'!=': (a, b) => !equals(a, b)
|
|
|
|
|
}[node.operator],
|
|
|
|
|
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 = parseResult.explanation.map(
|
|
|
|
|
cond([
|
2018-10-01 10:56:18 +00:00
|
|
|
|
[
|
|
|
|
|
propEq('category', 'variable'),
|
2018-10-01 15:41:55 +00:00
|
|
|
|
treatVariableTransforms(rules, rule)
|
2018-06-29 09:13:05 +00:00
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
propEq('category', 'value'),
|
|
|
|
|
node => ({
|
|
|
|
|
nodeValue: node.nodeValue,
|
|
|
|
|
// eslint-disable-next-line
|
|
|
|
|
jsx: nodeValue => <span className="value">{nodeValue}</span>
|
|
|
|
|
})
|
2018-07-30 15:51:51 +00:00
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
propEq('category', 'percentage'),
|
|
|
|
|
node => ({
|
|
|
|
|
nodeValue: node.nodeValue,
|
|
|
|
|
// eslint-disable-next-line
|
|
|
|
|
jsx: nodeValue => (
|
|
|
|
|
<span className="value">{nodeValue * 100}%</span>
|
|
|
|
|
)
|
|
|
|
|
//the best would be to display the original text before parsing, but nearley does'nt let us access it
|
|
|
|
|
})
|
2018-07-30 13:30:54 +00:00
|
|
|
|
]
|
2018-06-29 09:13:05 +00:00
|
|
|
|
])
|
|
|
|
|
),
|
|
|
|
|
operator = parseResult.operator
|
|
|
|
|
let operatorToUnicode = operator =>
|
|
|
|
|
({
|
|
|
|
|
'>=': '≥',
|
|
|
|
|
'<=': '≤',
|
|
|
|
|
'!=': '≠',
|
|
|
|
|
'*': '∗',
|
|
|
|
|
'/': '∕',
|
|
|
|
|
'-': '−'
|
|
|
|
|
}[operator] || operator)
|
|
|
|
|
let jsx = (nodeValue, explanation) => (
|
|
|
|
|
<Node
|
|
|
|
|
classes={'inlineExpression ' + parseResult.category}
|
|
|
|
|
value={nodeValue}
|
|
|
|
|
child={
|
|
|
|
|
<span className="nodeContent">
|
|
|
|
|
<span className="fa fa" />
|
|
|
|
|
{makeJsx(explanation[0])}
|
|
|
|
|
<span className="operator">
|
|
|
|
|
{operatorToUnicode(parseResult.operator)}
|
|
|
|
|
</span>
|
|
|
|
|
{makeJsx(explanation[1])}
|
|
|
|
|
</span>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
evaluate,
|
|
|
|
|
jsx,
|
|
|
|
|
operator,
|
|
|
|
|
text: rawNode,
|
|
|
|
|
category: parseResult.category,
|
|
|
|
|
type: parseResult.category == 'calcExpression' ? 'numeric' : 'boolean',
|
|
|
|
|
explanation
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-27 16:50:11 +00:00
|
|
|
|
export let treatNumber = rawNode => ({
|
|
|
|
|
text: '' + rawNode,
|
|
|
|
|
category: 'number',
|
|
|
|
|
nodeValue: rawNode,
|
|
|
|
|
type: 'numeric',
|
|
|
|
|
jsx: <span className="number">{rawNode}</span>
|
|
|
|
|
})
|
|
|
|
|
|
2018-06-29 09:13:05 +00:00
|
|
|
|
export let treatOther = rawNode => {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Cette donnée : ' + rawNode + ' doit être un Number, String ou Object'
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-09-30 18:38:57 +00:00
|
|
|
|
export let treatObject = (rules, rule, treatOptions) => rawNode => {
|
2018-06-29 09:13:05 +00:00
|
|
|
|
let mecanisms = intersection(keys(rawNode), keys(knownMecanisms))
|
|
|
|
|
|
|
|
|
|
if (mecanisms.length != 1) {
|
2018-07-09 12:13:38 +00:00
|
|
|
|
throw new Error(`OUPS : On ne devrait reconnaître que un et un seul mécanisme dans cet objet
|
|
|
|
|
Objet YAML : ${JSON.stringify(rawNode)}
|
|
|
|
|
Mécanismes implémentés correspondants : ${JSON.stringify(mecanisms)}
|
|
|
|
|
Cette liste doit avoir un et un seul élément.
|
|
|
|
|
Vérifier que le mécanisme est dans l'objet 'dispatch' et dans les'knownMecanisms.yaml'
|
|
|
|
|
`)
|
2018-06-29 09:13:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let k = head(mecanisms),
|
|
|
|
|
v = rawNode[k]
|
|
|
|
|
|
|
|
|
|
let dispatch = {
|
|
|
|
|
'une de ces conditions': mecanismOneOf,
|
|
|
|
|
'toutes ces conditions': mecanismAllOf,
|
|
|
|
|
'aiguillage numérique': mecanismNumericalSwitch,
|
|
|
|
|
somme: mecanismSum,
|
|
|
|
|
multiplication: mecanismProduct,
|
|
|
|
|
barème: mecanismScale,
|
2018-07-09 12:13:38 +00:00
|
|
|
|
'barème linéaire': mecanismLinearScale,
|
2018-12-05 17:19:11 +00:00
|
|
|
|
'barème continu': mecanismContinuousScale,
|
2018-06-29 09:13:05 +00:00
|
|
|
|
'le maximum de': mecanismMax,
|
|
|
|
|
'le minimum de': mecanismMin,
|
|
|
|
|
complément: mecanismComplement,
|
|
|
|
|
sélection: mecanismSelection,
|
|
|
|
|
'une possibilité': always({
|
|
|
|
|
...v,
|
|
|
|
|
'une possibilité': 'oui',
|
|
|
|
|
missingVariables: { [rule.dottedName]: 1 }
|
|
|
|
|
}),
|
|
|
|
|
inversion: mecanismInversion(rule.dottedName),
|
2018-08-07 16:20:08 +00:00
|
|
|
|
allègement: mecanismReduction,
|
2018-09-06 14:45:50 +00:00
|
|
|
|
variations: mecanismVariations,
|
|
|
|
|
synchronisation: mecanismSynchronisation
|
2018-06-29 09:13:05 +00:00
|
|
|
|
},
|
|
|
|
|
action = propOr(mecanismError, k, dispatch)
|
|
|
|
|
|
2018-09-30 18:38:57 +00:00
|
|
|
|
return action(treat(rules, rule, treatOptions), k, v)
|
2018-06-29 09:13:05 +00:00
|
|
|
|
}
|