lib/outils/basthon/analyse.js

/** @module lib/outils/basthon/analyse */
// cf commit 3e37ce84 (du 23/02/2022) pour une version qui utilise directement pyodide.runPython

/**
 * Retourne un message de diagnostic du code de l’élève (vide s’il est identique au code voulu), en utilisant directement le pyodide chargé par basthon
 * @param {Gui} gui
 * @param {string} codeRef Le code de référence (celui qui est dans l’éditeur devrait sortir la même chose en console que celui-ci)
 * @param {Object} variables la liste des variables à affecter avant de lancer le code de l’élève ou celui de référence
 * @return {Promise<string>}
 */
export async function getDiagMessage (gui, codeRef, variables) {
  // vire les lignes vides, les commentaires et les affectations de nos variables
  // (on aurait pu faire directement un replace sur la string sans l’éclater en lignes, mais c’est plus lisible donc moins buggé comme ça)
  const vars = Object.keys(variables)
  const stripVarRegexp = vars.length ? new RegExp(`^\\s*(${vars.join('|')})\\s*=`) : /./ // si pas de variables une regex qui match n’importe quel caractère
  const stripCommentRegexp = /^\s#/
  const cleanCode = code => code.split('\n').filter(line => !stripVarRegexp.test(line) && !stripCommentRegexp.test(line)).join('\n')
  await gui.loaded()
  // le script de l’élève
  const codeEleve = gui.getContent()
  // on démarre le code par l’affectation des variables
  const codeDebut = variables ? Object.entries(variables).map(([cle, valeur]) => `${cle} = ${valeur}`).join('\n') : ''
  // on ote les saisies de l’algo eleve
  const codeEleveTest = codeDebut + '\n' + cleanCode(codeEleve)
  // on ote les saisies de l’algo secret
  const codeRefTest = codeDebut + '\n' + cleanCode(codeRef)
  // et on l’exécute
  const { stdErr: stdErrRef, stdOut: stdOutRef } = await gui.eval(codeRefTest)
  if (stdErrRef) {
    console.error(stdErrRef)
    throw Error('Le code de référence fourni est invalide')
  }
  // on affiche le résultat du code de l’élève en console
  gui.shell.clear(true)
  gui.showShell()
  gui.shell.launch(codeEleveTest, false) // attention, ça émet un événement mais n’est pas une fct async, donc ça lance un truc qui va tourner en arrière plan
  // et on l’évalue
  const { stdErr, stdOut } = await gui.eval(codeEleveTest)
  // y’a un pb
  if (stdErr) {
    let diag = 'Ton programme a bogué pour ' + codeEleveTest
    const iErr = stdErr.lastIndexOf(' line ')
    if (iErr >= 0) {
      diag += '\n>>>' + stdErr.substring(iErr)
    }
    return diag
  }

  // pas d’erreurs, on regarde si la sortie est celle attendue
  if (stdOut === stdOutRef) return ''

  // on explique l’échec (pas d’erreur mais pas le résultat attendu)
  let diag = 'Résultat faux pour le code :\n' + codeEleveTest
  diag += '\n' + 'ta réponse : ' + stdOut
  diag += '\n' + 'la bonne réponse : ' + stdOutRef
  return diag
}