Javascript: Expression régulière pour analyser une formule

J'ai travaillé sur une fonction pour analyser une formule pendant un certain temps, mais je n'ai pas pu le faire fonctionner correctement. Il semble que cela ne fonctionne pas toujours – il filtre certaines parties du texte mais pas tous.

parseFormula(e) { var formula = e.value, value = 0.00, tValue = 0.00, tFormula = '', dObj = {}; if(formula !== undefined && formula !== "") { dObj._formulaIn = formula; var f = formula.split(/\s/g); for(var i = 0; i < f.length; i++) { tFormula = f[i]; // Replacing PI tFormula = tFormula.replace(/(pi)/gi,Math.PI); dObj._1_pi_done = tFormula; // Replacing Squareroot with placeholder tFormula = tFormula.replace(/(sqrt)/gi,"__sqrt__"); tFormula = tFormula.replace(/(sqr)/gi,"__sqrt__"); tFormula = tFormula.replace(/(kvrt)/gi,"__sqrt__"); tFormula = tFormula.replace(/(kvr)/gi,"__sqrt__"); dObj._2_sqrt_done = tFormula; // Removing units that may cause trouble tFormula = tFormula.replace(/(m2||m3||t2||t3||c2||c3)/gi,""); dObj._3_units_done = tFormula; // Removing text tFormula = tFormula.replace(/\D+[^\*\/\+\-]+[^\,\.]/gi,""); dObj._4_text_done = tFormula; // Removing language specific decimals if(Language.defaultLang === "no_NB") { tFormula = tFormula.replace(/(\.)/gi,""); tFormula = tFormula.replace(/(\,)/gi,"."); } else { tFormula = tFormula.replace(/(\,)/gi,""); } dObj._5_lang_done = tFormula; // Re-applying Squareroot tFormula = tFormula.replace(/(__sqrt__)/g,"Math.sqrt"); dObj._6_sqrt_done = tFormula; if(tFormula === "") { f.splice(i,1); } else { f[i] = tFormula; } dObj._7_splice_done = tFormula; console.log(dObj); } formula = ""; for(var j = 0; j < f.length; j++) { formula += f[j]; } try { value = eval(formula); } catch(err) {} return value === 0 ? 0 : value.toFixed(4); } else { return 0; } } 

Je ne suis pas sûr de l'utilisation de RegEx dans cette fonction, donc pourquoi je demande de l'aide. Par exemple, je ne sais pas si /(pi)/ est la bonne façon d'obtenir le texte exact "pi" et de le remplacer par 3.141.

(J'utilise eval pour le moment, mais c'est simplement utilisé pour le développement)

Toute aide appréciée.

Modifier:

La formule que je tente d'analyser est une formule d'entrée utilisateur. Où il / elle dactylographierait quelque chose comme: 2/0.6 pcs of foo * pi bar + sqrt(4) foobar . Où je voudrais qu'il enlève toutes les lettres non mathématiques et calcule le reste. Ce qui signifie que la formule ci-dessus serait interprétée comme (2/0.6) * 3.141 + Math.sqrt(4) => 12.47

Modifier 2:

e est un objet ExtJS, transmis par un champ dans une grille, il contient les variables suivantes:

  • colIdx (int)
  • column ( Ext.grid.column.Column )
  • field (chaîne)
  • grid ( Ext.grid.Panel )
  • originalValue (chaîne)
  • record ( Ext.data.Model )
  • row (sélecteur CSS)
  • rowIdx (int)
  • store ( Ext.data.Store )
  • value (chaîne)
  • view ( Ext.grid.View )

Je ne suis actuellement pas en mesure de faire en sorte que JSFiddle fonctionne correctement.

Il est probablement plus facile d'analyser l'expression que vous souhaitez analyser. Lorsque tokenized, il est plus facile de lire ce flux de jetons et de créer vos propres expressions.

J'ai mis en place une démonstration sur jsFiddle qui peut analyser votre formule donnée

Dans la démo, j'ai utilisé cette classe et jetons TokenStream pour créer un TokenStream partir de la formule.

 function Tokenizer() { this.tokens = {}; // The regular expression which matches a token per group. this.regex = null; // Holds the names of the tokens. Index matches group. See buildExpression() this.tokenNames = []; } Tokenizer.prototype = { addToken: function(name, expression) { this.tokens[name] = expression; }, tokenize: function(data) { this.buildExpression(data); var tokens = this.findTokens(data); return new TokenStream(tokens); }, buildExpression: function (data) { var tokenRegex = []; for (var tokenName in this.tokens) { this.tokenNames.push(tokenName); tokenRegex.push('('+this.tokens[tokenName]+')'); } this.regex = new RegExp(tokenRegex.join('|'), 'g'); }, findTokens: function(data) { var tokens = []; var match; while ((match = this.regex.exec(data)) !== null) { if (match == undefined) { continue; } for (var group = 1; group < match.length; group++) { if (!match[group]) continue; tokens.push({ name: this.tokenNames[group - 1], data: match[group] }); } } return tokens; } } TokenStream = function (tokens) { this.cursor = 0; this.tokens = tokens; } TokenStream.prototype = { next: function () { return this.tokens[this.cursor++]; }, peek: function (direction) { if (direction === undefined) { direction = 0; } return this.tokens[this.cursor + direction]; } } 

Jetons définis

 tokenizer.addToken('whitespace', '\\s+'); tokenizer.addToken('l_paren', '\\('); tokenizer.addToken('r_paren', '\\)'); tokenizer.addToken('float', '[0-9]+\\.[0-9]+'); tokenizer.addToken('int', '[0-9]+'); tokenizer.addToken('div', '\\/'); tokenizer.addToken('mul', '\\*'); tokenizer.addToken('add', '\\+'); tokenizer.addToken('constant', 'pi|PI'); tokenizer.addToken('id', '[a-zA-Z_][a-zA-Z0-9_]*'); 

Avec les jetons ci-dessus définis, le tokenizer peut tout reconnaître dans votre formule. Lorsque la formule

 2/0.6 pcs of foo * pi bar + sqrt(4) foobar 

Est-ce que le résultat serait un jeton similaire à celui de

 int(2), div(/), float(0.6), whitespace( ), id(pcs), whitespace( ), id(of), whitespace( ), id(foo), whitespace( ), mul(*), whitespace( ), constant(pi), whitespace( ), id(bar), whitespace( ), add(+), whitespace( ), id(sqrt), l_paren((), int(4), r_paren()), whitespace( ), id(foobar) 

Vous ne pouvez pas vraiment utiliser une expression régulière pour correspondre à une formule. Les formules sont un langage sans contexte et les expressions régulières sont limitées aux langues habituelles , ce dernier étant un sous-ensemble du premier. Il existe un certain nombre d'algorithmes pour reconnaître des langages sans contexte tels que les analyseurs CYK et LL . Je ne recommande pas d'étudier ceux-là si vous ne l'avez déjà pas puisque le sujet est assez important.

Ce que vous pouvez faire rapidement, efficacement et facilement, c'est essayer de calculer la formule à l'aide de la Notation polaire inversée (RPN) (utilisez l' algorithme Shunting Yard pour convertir votre formule en RPN). Si la tentative échoue (en raison de parenthèses sans maching, de fonctions / constantes non valides, w / e), clairement le texte n'est pas une formule, sinon tout est bon. Shunting yard n'est pas un algorithme particulièrement difficile et vous ne devriez pas avoir du mal à le mettre en œuvre. Même si vous le faites, la page wikipedia que j'ai liée ci-dessus a un pseudo-code et il y a un bon nombre de questions en SO pour vous aider.