retour au cours

Parsez une expression

Une expression arithmétique consiste en 2 nombres et un opérateur entre les deux, par exemple :

  • 1 + 2
  • 1.2 * 3.4
  • -3 / -6
  • -2 - 2

L’opérateur l’un des : "+", "-", "*" ou "/".

Il peut y avoir des espaces supplémentaires au début, à la fin ou entre chaque partie.

Créez une fonction parse(expr) qui prend une expression et retourne un tableau de trois éléments :

  1. Le premier nombre.
  2. L’opérateur.
  3. Le second nombre.

Par exemple :

let [a, op, b] = parse("1.2 * 3.4");

alert(a); // 1.2
alert(op); // *
alert(b); // 3.4

Une regexp pour un nombre : -?\d+(\.\d+)?. Nous l’avons vu dans l’exercice précédent.

Pour l’opérateur [-+*/]. Le tiret - est en premier, car il pourrait signifier un intervalle de caractère, alors que nous souhaitons juste le caractère -.

Le slash / doit être échappé en javascript dans une regexp /.../, et nous le ferons plus tard.

Nous cherchons un nombre, un opérateur puis un autre nombre. Et d’éventuels espaces entre eux.

Cela done l’expression régulière : -?\d+(\.\d+)?\s*[-+*/]\s*-?\d+(\.\d+)?.

Il y a trois parties, avec \s* entre elles :

  1. -?\d+(\.\d+)? – le premier nombre,
  2. [-+*/] – l’opérateur,
  3. -?\d+(\.\d+)? – le deuxième nombre.

Pour faire de chacune de ces parties un élément distinct du tableau de correspondance, entourons-les de parenthèses : (-?\d+(\.\d+)?)\s*([-+*/])\s*(-?\d+(\.\d+)?).

Cela donne :

let regexp = /(-?\d+(\.\d+)?)\s*([-+*\/])\s*(-?\d+(\.\d+)?)/;

alert( "1.2 + 12".match(regexp) );

Le résultat inclus :

  • result[0] == "1.2 + 12" (la correspondance complète)
  • result[1] == "1.2" (premier groupe (-?\d+(\.\d+)?) – le premier nombre, avec la partie décimale)
  • result[2] == ".2" (second groupe(\.\d+)? – la première partie décimale)
  • result[3] == "+" (troisième groupe ([-+*\/]) – l’opérateur)
  • result[4] == "12" (quatrième groupe (-?\d+(\.\d+)?) – le second nombre)
  • result[5] == undefined (cinquième groupe (\.\d+)? – la deuxième partie décimale est absente, c’est non défini)

Nous ne souhaitons que les nombres et l’opérateur, sans la correspondance entière, ni les parties décimales. Faisons alors un peu le ménage.

La correspondance complète(le premier élément du tableau) peut être enlevée par result.shift().

Les groupes contenant les parties décimales(groupes 2 et 4) (.\d+) peuvent être exclus en ajoutant ?: au début : (?:\.\d+)?.

La solution complète :

function parse(expr) {
  let regexp = /(-?\d+(?:\.\d+)?)\s*([-+*\/])\s*(-?\d+(?:\.\d+)?)/;

  let result = expr.match(regexp);

  if (!result) return [];
  result.shift();

  return result;
}

alert( parse("-1.23 * 3.45") );  // -1.23, *, 3.45

As an alternative to using the non-capturing ?:, we could name the groups, like this:

function parse(expr) {
  let regexp = /(?<a>-?\d+(?:\.\d+)?)\s*(?<operator>[-+*\/])\s*(?<b>-?\d+(?:\.\d+)?)/;

  let result = expr.match(regexp);

  return [result.groups.a, result.groups.operator, result.groups.b];
}

alert( parse("-1.23 * 3.45") );  // -1.23, *, 3.45;