1
0
Fork 0
mirror of https://github.com/betagouv/mon-entreprise synced 2025-02-09 02:55:01 +00:00
mon-entreprise/source/engine/grammar.ne
Maxime Quandalle 648fb249ed ⚙️ Meilleure grammaire pour les unités litérales
Deplacement de la détection de la règle "words" (ie une suite de mots)
du lexer/tokenizer vers le parser. Le lexer se contente de donner les
mots un à un, et une liste de "words" est détectée via une règle Nearley.

Cela permet de corriger l'ambiguité entre unités et noms de variables qui
est dépendante du contexte et ne peut donc pas être levée au niveau du lexer.
2019-10-29 11:40:16 +01:00

104 lines
No EOL
3.2 KiB
Text

# This grammar is inspired by the "fancier grammar" tab of the nearley playground : https://omrelli.ug/nearley-playground
# Look for the PEMDAS system : Parentheses, Exponents (omitted here), Multiplication, and you should guess the rest :)
# This preprocessor was disabled because it doesn't work with Jest
# @preprocessor esmodule
@{%
const {string, filteredVariable, variable, temporalVariable, binaryOperation, unaryOperation, boolean, number, numberWithUnit, percentage } = require('./grammarFunctions')
const moo = require("moo");
const letter = '[a-zA-Z\u00C0-\u017F]';
const letterOrNumber = '[a-zA-Z\u00C0-\u017F0-9\']';
const word = `${letter}(?:[\-']?${letterOrNumber}+)*`;
const numberRegExp = '-?(?:[1-9][0-9]+|[0-9])(?:\.[0-9]+)?';
const percentageRegExp = numberRegExp + '\\%'
const lexer = moo.compile({
percentage: new RegExp(percentageRegExp),
number: new RegExp(numberRegExp),
'(': '(',
')': ')',
'[': '[',
']': ']',
comparison: ['>','<','>=','<=','=','!='],
additionSubstraction: /[\+-]/,
multiplicationDivision: ['*','/'],
word: new RegExp(word),
string: /'[ \t\.'a-zA-Z\-\u00C0-\u017F0-9 ]+'/,
'€': '€',
dot: ' . ',
letterOrNumber: new RegExp(letterOrNumber),
space: { match: /[\s]+/, lineBreaks: true }
});
const join = (args) => ({value: (args.map(x => x && x.value).join(""))})
const flattenJoin = ([a, b]) => Array.isArray(b) ? join([a, ...b]) : a
%}
@lexer lexer
main ->
AdditionSubstraction {% id %}
| Comparison {% id %}
| NonNumericTerminal {% id %}
| Negation {% id %}
NumericTerminal ->
Variable {% id %}
| TemporalVariable {% id %}
| FilteredVariable {% id %}
| number {% id %}
Negation ->
"-" %space Parentheses {% unaryOperation('calculation') %}
Parentheses ->
"(" AdditionSubstraction ")" {% ([,e]) => e %}
| "(" Negation ")" {% ([,e]) => e %}
| NumericTerminal {% id %}
Comparison -> Comparable %space %comparison %space Comparable {% binaryOperation('comparison')%}
Comparable -> ( AdditionSubstraction | NonNumericTerminal) {% ([[e]]) => e %}
NonNumericTerminal ->
boolean {% id %}
| string {% id %}
Words -> %word (%space (%word {% id %} | %letterOrNumber {% id %}) {% join %}):* {% flattenJoin %}
Variable -> Words (%dot Words {% ([,words]) => words %}):* {% variable %}
BaseUnit ->
%word {% id %}
| "€" {% id %}
Unit -> BaseUnit ("/" BaseUnit {% join %}):? {% join %}
Filter -> "[" Words "]" {% ([,filter]) => filter %}
FilteredVariable -> Variable %space Filter {% filteredVariable %}
TemporalTransform -> "[" ("mensuel" | "annuel" {% id %}) "]" {% ([,temporality]) => temporality %}
TemporalVariable -> Variable %space TemporalTransform {% temporalVariable %}
AdditionSubstraction ->
AdditionSubstraction %space %additionSubstraction %space MultiplicationDivision {% binaryOperation('calculation') %}
| MultiplicationDivision {% id %}
MultiplicationDivision ->
MultiplicationDivision %space %multiplicationDivision %space Parentheses {% binaryOperation('calculation') %}
| Parentheses {% id %}
boolean ->
"oui" {% boolean(true) %}
| "non" {% boolean(false) %}
number ->
%number {% number %}
| %number %space Unit {% numberWithUnit %}
| %percentage {% percentage %}
string -> %string {% string %}