diff --git a/inc-arg-squel.php3 b/inc-arg-squel.php3
index 02704bba603031cd4b74b38064555b437a0f0b2a..87797b1403d543644fd802bb4c85cd0cd9ff84ff 100644
--- a/inc-arg-squel.php3
+++ b/inc-arg-squel.php3
@@ -26,10 +26,9 @@ function calculer_params($idb, &$boucles) {
 				$params2[]=$param;
 		$params = $params2;
 
-		$boucle->hierarchie = '
-		$hierarchie = calculer_hierarchie('
+		$boucle->hierarchie = '$hierarchie = calculer_hierarchie('
 		.calculer_argument_precedent($idb, 'id_rubrique', $boucles)
-		.", false);\n";
+		.', false);';
 	}
 
 
diff --git a/inc-calcul-squel.php3 b/inc-calcul-squel.php3
index deb7fc980b4bff4beaa2cd370a3ce6237e7d9c9e..76f1e10e9695198e986e98cfbe57d18e5cc106b6 100644
--- a/inc-calcul-squel.php3
+++ b/inc-calcul-squel.php3
@@ -1,9 +1,10 @@
 <?php
+
 // Ce fichier ne sera execute qu'une fois
 if (defined("_INC_CALCUL_SQUEL")) return;
 define("_INC_CALCUL_SQUEL", "1");
 
-# Fichier principal du compilateur de squelettes, incluant tous les autres.
+// Fichier principal du compilateur de squelettes, incluant tous les autres.
 
 include_local("inc-bcl-squel.php3");
 include_local("inc-arg-squel.php3");
@@ -16,27 +17,21 @@ include_local("inc-index-squel.php3");
 include_local("inc-text-squel.php3");
 include_local("inc-debug.php3");
 
-# Produit le corps PHP d'une boucle Spip,
-# essentiellement une boucle while (ou une double en cas de hierarchie)
-# remplissant une variable $t0 retourne'e en valeur
+// Produit le corps PHP d'une boucle Spip,
+// essentiellement une boucle while (ou une double en cas de hierarchie)
+// remplissant une variable $t0 retourne'e en valeur
 
-function calculer_boucle($id_boucle, &$boucles)
-{
-  global $table_primary, $table_des_tables; 
+function calculer_boucle($id_boucle, &$boucles) {
+	global $table_primary, $table_des_tables; 
 
 	$boucle = &$boucles[$id_boucle];
 	$type_boucle = $boucle->type_requete;
 
- 	list($return,$corps) = $boucle->return;
+	list($return,$corps) = $boucle->return;
 
 	// Boucle recursive : simplement appeler la boucle interieure
-
 	if ($type_boucle == 'boucle')
-	  {
-	    return ("$corps\n\treturn  $return;");
-	}
-
-	$constant = ereg("^'[^']*'$",$return);
+	    return ("$corps\n	return $return;");
 
 	// La boucle doit-elle selectionner la langue ?
 	// 1. par defaut 
@@ -53,124 +48,162 @@ function calculer_boucle($id_boucle, &$boucles)
 	if ($lang_select)
 		$boucle->select[] = (($id_table = $table_des_tables[$type_boucle]) ? $id_table.'.' : '') .'lang';
 
-	$flag_parties = ($boucle->partie AND $boucle->total_parties);
-	$flag_cpt = $flag_parties || # pas '$compteur' a` cause du cas 0
-	  		strpos($corps,'compteur_boucle') ||
-	  		strpos($return,'compteur_boucle');
+	// Qui sommes-nous ?
 	$primary_key = $table_primary[$type_boucle];
 
-# invalidation des caches si c'est une boucle SPIP, non constante de surcroit
+	// Calculer les invalideurs si c'est une boucle non constante
+	$constant = ereg("^'[^']*'$",$return);
 	if ((!$primary_key) || $constant)
-	  $invalide = '';
-	else
-	  {
-	    $id_table = $table_des_tables[$type_boucle]; 
-	    $boucle->select[] = "$id_table.$primary_key";
-
-		if ($primary_key == 'id_forum')
-			$invalide = 'include_ecrire("inc_forum.php3");';
-	    $invalide .= '
-		$Cache["' . $primary_key . '"][' .
-	      (($primary_key != 'id_forum') ?
-	       ('$Pile[$SP]["'  .  $primary_key . '"]') :
-	       ('calcul_index_forum(' . 
-# Retournera 4 [$SP] mais force la demande du champ au serveur SQL
-		index_pile($id_boucle, 'id_article', $boucles) . ',' .
-		index_pile($id_boucle, 'id_breve', $boucles) .  ',' .
-		index_pile($id_boucle, 'id_rubrique', $boucles) .',' .
-		index_pile($id_boucle, 'id_syndic', $boucles) .  ')')) .
-	      '] = 1;';
-	  }
-	$debut =
-	  ((!$flag_cpt) ? "" : "\n\t\t\$compteur_boucle++;") .
-	  ((!$flag_parties) ? "" : '
-		if	($compteur_boucle >= $debut_boucle AND 
-			 $compteur_boucle <= $fin_boucle) {') .
-	  (((!$lang_select)||($constant)) ? "" : ('
-		if ($x = $Pile[$SP]["lang"]) $GLOBALS["spip_lang"] = $x;')) .
-	  $invalide .
-	  ((!$boucle->doublons) ? "" : 
-	   ("\n\t\t\$doublons['$type_boucle'] .= ','. " .
-	    index_pile($id_boucle, $primary_key, $boucles) .
-	    ";")) ;
-	  $corps = $debut . $corps .
-	  ((!$boucle->separateur) ? 
-	   (($constant && !$debut) ? $return : ("\n\t\t" . '$t0 .= ' . $return . ";")) :
-	   ("\n\t\t" . '$t1 = ' . $return . ";\n\t\t" .
-	     '$t0 .= (($t1 && $t0) ? \'' . $boucle->separateur .
-	    "' : '') . \$t1;")).
-	  ((!$flag_parties) ? "" : "\t\t}\n");
-
-
-	// Initialisation du $texte
-	$texte = '';
+		$invalide = '';
+	else {
+		$id_table = $table_des_tables[$type_boucle]; 
+		$boucle->select[] = "$id_table.$primary_key";
+
+		$invalide = "\n			\$Cache['$primary_key']";
+		if ($primary_key != 'id_forum')
+			$invalide .= "[\$Pile[\$SP]['$primary_key']] = 1;";
+		else
+			$invalide .= "[calcul_index_forum(" . 
+				// Retournera 4 [$SP] mais force la demande du champ a MySQL
+				index_pile($id_boucle, 'id_article', $boucles) . ',' .
+				index_pile($id_boucle, 'id_breve', $boucles) .  ',' .
+				index_pile($id_boucle, 'id_rubrique', $boucles) .',' .
+				index_pile($id_boucle, 'id_syndic', $boucles) .  ")] = 1;";
+		$invalide .= ' // invalideurs';
+	}
 
-	# hack doublons documents : s'il y a quelque chose dans
-	# $GLOBALS['doublons_documents'], c'est que des documents ont
-	# ete vus par integre_image() ou autre fournisseur officiel de
-	# doublons : on les transfere alors vers la vraie variable
+
+	// Cas {1/3} {1,4} {n-2,1}...
+	$flag_parties = ($boucle->partie AND $boucle->total_parties);
+	$flag_cpt = $flag_parties || // pas '$compteur' a cause du cas 0
+		strpos($corps,'compteur_boucle') ||
+		strpos($return,'compteur_boucle');
+
+	//
+	// Creer le debut du corps de la boucle :
+	//
+	if ($flag_cpt)
+		$debut = "\n		\$compteur_boucle++;";
+
+	if ($flag_parties)
+		$debut .= '
+		if ($compteur_boucle >= $debut_boucle
+		AND $compteur_boucle <= $fin_boucle) {';
+	
+	if ($lang_select AND !$constant)
+		$debut .= '
+			if ($x = $Pile[$SP]["lang"]) $GLOBALS["spip_lang"] = $x; // langue';
+
+	$debut .= $invalide;
+
+	if ($boucle->doublons)
+		$debut .= "\n			\$doublons['$type_boucle'] .= ','. " .
+		index_pile($id_boucle, $primary_key, $boucles) . "; // doublons";
+
+
+	//
+	// L'ajouter au corps
+	//
+	$corps = $debut . $corps;
+
+	// Separateur ?
+	if ($boucle->separateur) {
+		$corps .= "\n			\$t1 = $return;
+		\$t0 .= ((\$t1 && \$t0) ? '"
+		. $boucle->separateur
+		. "' : '')
+		. \$t1;";
+	} else if ($constant && !$debut) {
+		$corps .= $return;
+	} else {
+		$corps .= "\n			\$t0 .= $return;";
+	}
+
+	// Fin de parties
+	if ($flag_parties)
+		$corps .= "\n		}\n";
+
+
+	// Gestion de la hierarchie (voir inc-arg-squel)
+	if ($boucle->hierarchie)
+		$texte .= "\n	".$boucle->hierarchie;
+
+	// hack doublons documents : s'il y a quelque chose dans
+	// $GLOBALS['doublons_documents'], c'est que des documents ont
+	// ete vus par integre_image() ou autre fournisseur officiel de
+	// doublons : on les transfere alors vers la vraie variable
 	$texte .= '
-		$doublons[\'documents\'] .= $GLOBALS[\'doublons_documents\'];
-		unset($GLOBALS[\'doublons_documents\']);';
+	$doublons[\'documents\'] .= $GLOBALS[\'doublons_documents\'];
+	unset($GLOBALS[\'doublons_documents\']);';
 
 	// Recherche : recuperer les hash a partir de la chaine de recherche
 	if ($boucle->hash) {
 		$texte .=  '
-		list($hash_recherche, $hash_recherche_strict) = requete_hash($GLOBALS["recherche"]);
-		if (!$hash_recherche) $stop_query = true;';
+	// RECHERCHE
+	list($hash_recherche, $hash_recherche_strict) = requete_hash($GLOBALS["recherche"]);';
 	}
 
-	// Gestion de la hierarchie (voir inc-arg-squel)
-	$texte .= $boucle->hierarchie;
-
-	# si le corps est une constante, ne plus appeler le serveur
-	if (!ereg("^'[^']*'$",$corps))
-	  {
-	    $corps = '
-	while ($Pile[$SP] = @spip_fetch_array($result)) ' . 
-	    "\n\t\{$corps\n\t}";
-	    if ($lang_select) {
-	      $corps = '
-	$old_lang = $GLOBALS[\'spip_lang\'];' .
-		   $corps . '
-	$GLOBALS["spip_lang"] = $old_lang;';
+	// si le corps est une constante, ne plus appeler le serveur
+	if (ereg("^'[^']*'$",$corps)) {
+		// vide ?
+		if ($corps == "''") {
+			if (!$boucle->numrows)
+				return 'return "";';
+			else
+				$corps = "";
+		} else {
+			$boucle->numrows = true;
+			$corps = "\n		".'for($x=$Numrows["'.$id_boucle.'"];$x>0;$x--)
+			$t0 .= ' . $corps .';';
 	    }
-	  } else {
-	  if ($corps != "''")
-	    {
-	      $boucle->numrows = true;
-	      $corps = '
-	for($x=$Numrows["' . $id_boucle . '"];$x>0;$x--) $t0.= ' . $corps .';';
-	    }
-	  else if (!$boucle->numrows) return 'return "";'; else $corps = "";
+	} else {
+		$corps = '
+
+	// RESULTATS
+	while ($objet = @spip_fetch_array($result)) {'
+	. "\n\t\t\$Pile[\$SP] = \$objet;"
+	. "\n$corps\n	}\n";
+
+		// Memoriser la langue avant la boucle pour la restituer apres
+		if ($lang_select) {
+			$texte .= "\n	\$old_lang = \$GLOBALS['spip_lang'];";
+			$corps .= "\n	\$GLOBALS['spip_lang'] = \$old_lang;";
+		}
 	}
 
-	return  ($texte . '
-	if (!$stop_query) $result = ' . calculer_requete($boucle) . '
-	$t0 = "";
-	$SP++;' .
-		 (($flag_parties) ? 
-		  calculer_parties($boucle->partie,
-				   $boucle->mode_partie,
-				   $boucle->total_parties,
-				   $id_boucle) :
-		  ((!$boucle->numrows) ? '' : "
-	\$Numrows['$id_boucle'] = @spip_num_rows(\$result);")) .
-		 ((!$flag_cpt) ? '' : "\n\t\$compteur_boucle = 0;") .
-		$corps .
-		'
-	@spip_free_result($result);' .
-		 (!($flag_h) ? '
-	return $t0;' : ('
-	$SP--;
-	$h0 = $t0 .' .
-		   ((!$boucle->separateur) ? "" :
-		    ('(($h0 && $t0) ? \'' . $boucle->separateur . "' : '') .")) .
-		   ' $h0;}
-	return $h0;')));
+	//
+	// Requete
+	//
+	$init = "\n\n	// REQUETE\n	";
+
+	// hack critere recherche : ignorer la requete en cas de hash vide
+	if ($boucle->hash)
+		$init .= "if (\$hash_recherche) ";
+
+	$init .= "\$result = " . calculer_requete($boucle);
+	$init .= "\n	".'$t0 = "";
+	$SP++;';
+	if ($flag_cpt)
+		$init .= "\n	\$compteur_boucle = 0;";
+
+
+	if ($flag_parties)
+		$init .= calculer_parties($boucle->partie,
+			$boucle->mode_partie,
+			$boucle->total_parties,
+			$id_boucle);
+	else if ($boucle->numrows)
+		$init .= "\n	\$Numrows['$id_boucle'] = @spip_num_rows(\$result);";
+
+	//
+	// Conclusion et retour
+	//
+	$conclusion = "\n	@spip_free_result(\$result);";
+	$conclusion .= "\n	return \$t0;";
+
+	return $texte . $init . $corps . $conclusion;
 }
 
-// une grosse fonction pour un petit cas
 
 function calculer_parties($partie, $mode_partie, $total_parties, $id_boucle) {
 
@@ -179,277 +212,311 @@ function calculer_parties($partie, $mode_partie, $total_parties, $id_boucle) {
 	// et du dernier demandes dans la boucle : 0 pour le premier,
 	// n-1 pour le dernier ; donc total_boucle = 1 + debut - fin
 
-	# nombre total avant partition
-	$retour = "\n\t".'$nombre_boucle = @spip_num_rows($result);';
+	// nombre total avant partition
+	$retour = "\n\n	// Partition\n	"
+	.'$nombre_boucle = @spip_num_rows($result);';
 
 	ereg("([+-/])([+-/])?", $mode_partie, $regs);
 	list(,$op1,$op2) = $regs;
 
-	# {1/3}
+	// {1/3}
 	if ($op1 == '/') {
-		$retour .= "\n\t"
+		$retour .= "\n	"
 			.'$debut_boucle = 1 + ceil(($nombre_boucle * '
-			. ($partie - 1) . ')/' . $total_parties . ");\n\t"
+			. ($partie - 1) . ')/' . $total_parties . ");\n	"
 			. '$fin_boucle = ceil (($nombre_boucle * '
 			. $partie . ')/' . $total_parties . ");";
 	}
 
-	# {1,x}
+	// {1,x}
 	if ($op1 == '+') {
-		$retour .= "\n\t"
+		$retour .= "\n	"
 			. '$debut_boucle = ' . $partie . ';';
 	}
-	# {n-1,x}
+	// {n-1,x}
 	if ($op1 == '-') {
-		$retour .= "\n\t"
+		$retour .= "\n	"
 			. '$debut_boucle = $nombre_boucle - ' . $partie . ';';
 	}
-	# {x,1}
+	// {x,1}
 	if ($op2 == '+') {
-		$retour .= "\n\t"
+		$retour .= "\n	"
 			. '$fin_boucle = $debut_boucle + ' . $partie . ' - 1;';
 	}
-	# {x,n-1}
+	// {x,n-1}
 	if ($op2 == '-') {
-		$retour .= "\n\t"
+		$retour .= "\n	"
 			. '$fin_boucle = $debut_boucle+($nombre_boucle-'.$partie.')-1;';
 	}
 
-	# Rabattre $fin_boucle sur le maximum
-	$retour .= "\n\t"
+	// Rabattre $fin_boucle sur le maximum
+	$retour .= "\n	"
 		.'$fin_boucle = min($fin_boucle, $nombre_boucle);';
 
-	# calcul du total boucle final
-	$retour .= "\n\t"
+	// calcul du total boucle final
+	$retour .= "\n	"
 		.'$Numrows[\''.$id_boucle.'\'] = $fin_boucle - $debut_boucle + 1;';
 
 	return $retour;
 }
 
 
-# Production du code PHP a` partir de la se'quence livre'e par le phraseur
-# $boucles est passe' par re'fe'rence pour affectation par index_pile.
-# Retourne un tableau de 2 e'le'ments: 
-# 1. une expression PHP,
-# 2. une suite d'instructions PHP a` exe'cuter avant d'e'valuer l'expression.
-# si cette suite est vide, on fusionne les se'quences d'expressions
-# ce qui doit re'duire la me'moire ne'cessaire au processus
-# En de'coule une combinatoire laborieuse mais sans difficulte'
+// Production du code PHP a` partir de la se'quence livre'e par le phraseur
+// $boucles est passe' par re'fe'rence pour affectation par index_pile.
+// Retourne un tableau de 2 e'le'ments: 
+// 1. une expression PHP,
+// 2. une suite d'instructions PHP a` exe'cuter avant d'e'valuer l'expression.
+// si cette suite est vide, on fusionne les se'quences d'expressions
+// ce qui doit re'duire la me'moire ne'cessaire au processus
+// En de'coule une combinatoire laborieuse mais sans difficulte'
 
-function calculer_liste($tableau, $prefix, $id_boucle, $niv, &$boucles, $id_mere)
-{
+function calculer_liste($tableau, $prefix, $id_boucle, $niv, &$boucles, $id_mere) {
 	if ((!$tableau)) return array("''",'');
 	$texte = '';
 	$exp = "";
 	$process_ins = false;
 	$firstset = true;
 	$t = '$t' . ($niv+1);
-	reset($tableau);
-	while (list(, $objet) = each($tableau)) {
-	  if ($objet->type == 'texte') {
-	    $c = calculer_texte($objet->texte,$id_boucle, $boucles, $id_mere);
-	    if (!$exp)
-	      $exp = $c;
-	    else 
-	      {if ((substr($exp,-1)=="'") && (substr($c,1,1)=="'")) 
-		  $exp = substr($exp,0,-1) . substr($c,2);
-		else
-		  $exp .= (!$exp ? $c :  (" .\n\t\t$c"));}
-	    if (!(strpos($c,'<?') === false)) $pi = true;
-	  } else {
-	  if ($objet->type == 'include') {
-	    $c = calculer_inclure($objet->fichier,
-				  $objet->params,
-				  $id_boucle,
-				  $boucles);
-	    $exp .= (!$exp ? $c : (" .\n\t\t$c"));
-	  } else {
-	    if ($objet->type ==  'boucle') {
-		$nom = $objet->id_boucle;
-		list($bc,$bm) = calculer_liste($objet->cond_avant, $prefix,
-					       $id_boucle, $niv+2, $boucles, $nom);
-		list($ac,$am) = calculer_liste($objet->cond_apres, $prefix,
-					       $id_boucle, $niv+2, $boucles, $nom);
-		list($oc,$om) = calculer_liste($objet->cond_altern, $prefix,
-					       $id_boucle, $niv+1,$boucles, $nom);
-	      $c = $prefix .
-		ereg_replace("-","_", $nom) .
-		'($Cache, $Pile, $doublons, $Numrows, $SP)';
-	      $m = "";
-	    } else {
-	      list($c,$m) = 
-		  calculer_champ($objet->fonctions, 
-				 $objet->nom_champ,
-				 $id_boucle,
-				 $boucles,
-				 $id_mere);
-		list($bc,$bm) = calculer_liste($objet->cond_avant, $prefix, $id_boucle, $niv+2,$boucles, $id_mere); 
-	      	list($ac,$am) = calculer_liste($objet->cond_apres, $prefix, $id_boucle, $niv+2,$boucles, $id_mere);
-		$oc = "''";
-		$om = "";
-	    }
-	    // traitement commun des champs et boucles.
-	    // Produit:
-	    // m ; if (Tniv+1 = v)
-	    // { bm; Tniv+1 = $bc . Tniv+1; am; Tniv+1 .= $ac }
-	    // else { om; $Tniv+1 = $oc }
-	    // Tniv .= Tniv+1
-	    // Optimisations si une au moins des 4 se'quences $*m  est vide
-
-	    if ($m) {
-			$texte .= "\n\n\t\t# MILIEU\n";
-			// il faut achever le traitement de l'exp pre'ce'dente
-	      if ($exp) {
-		  $texte .= "\t\t\$t$niv " .
-		    (($firstset) ? "=" : ".=") .
-		    "$exp;$m" ;
-		  $firstset = false;
-		  $exp = "";
-	      } else { $texte .= $m;}
-	    }
-		$texte .= "\n\n\t\t# CODE\n";
-	    if (!($bm || $am || $om)) {
-	      // 3 se'quences vides: 'if' inutile
-	      $a = (($bc == "''") ?  "" : "$bc .") .
-		$t .
-		(($ac == "''") ?  "" : " . $ac");
-	      // s'il y a un avant ou un apre`s ou un alternant, il faut '?'
-	      if (($a != $t) || ($oc != "''"))
-		{ $c = "(($t = $c) ? ($a) : ($oc))"; }
-	      $exp = (!$exp ? $c : ("$exp .\n\t\t$c"));
-	    } else {
-	      // il faut achever le traitement de l'exp pre'ce'dente
-	      if ($exp) {
-		  $texte .= "\n\t\t\$t$niv " .
-		    (($firstset) ? "=" : ".=") .
-		    "$exp;" ;
-		  $firstset = false;
-	      }
-	      $exp = (($bc == "''") ? $t : "($bc . $t)");
-	      $texte .= "\n\t\tif ($t = $c) \n{" . $bm;
-	      if ($am) {
-		$texte .= "$t = $exp;\n\t$am";
-		$exp = $t; }
-	      if ($ac != "''") $exp = "($exp . $ac)";
-	      $texte .= (($exp == $t) ? '' : ("$t = $exp;")) . ";}";
-	      if ($om || ($oc != "''"))
-		{ $texte .= " else {" . $om . "$t =  ($oc);}"; }
-	      $exp = $t;
-	    }
-	  }
-	  }
-	} // while
-	if (!$exp) $exp ="''";
+
+	foreach ($tableau as $objet) {
+
+		switch($objet->type) {
+		// texte seul
+		case 'texte':
+			$c = calculer_texte($objet->texte,$id_boucle, $boucles, $id_mere);
+			if (!$exp)
+				$exp = $c;
+			else {
+				if ((substr($exp,-1)=="'") && (substr($c,1,1)=="'")) 
+					$exp = substr($exp,0,-1) . substr($c,2);
+				else
+					$exp .= (!$exp ? $c :  (" .\n		$c"));
+			}
+			if (!(strpos($c,'<'.'?') === false))
+				$pi = true;
+			$traitement_champ = false;
+			break;
+
+		// inclure
+		case 'include':
+			$c = calculer_inclure($objet->fichier,
+				$objet->params,
+				$id_boucle,
+				$boucles);
+			$exp .= (!$exp ? $c : (" .\n		$c"));
+			$traitement_champ = false;
+			break;
+
+		// boucle
+		case 'boucle':
+			$nom = $objet->id_boucle;
+			list($bc,$bm) = calculer_liste($objet->cond_avant, $prefix,
+				$id_boucle, $niv+2, $boucles, $nom);
+			list($ac,$am) = calculer_liste($objet->cond_apres, $prefix,
+				$id_boucle, $niv+2, $boucles, $nom);
+			list($oc,$om) = calculer_liste($objet->cond_altern, $prefix,
+				$id_boucle, $niv+1,$boucles, $nom);
+			$c = $prefix . ereg_replace("-","_", $nom)
+			. '($Cache, $Pile, $doublons, $Numrows, $SP)';
+			$m = "";
+			$traitement_champ = true;
+			$commentaire = "/* BOUCLE$nom */";
+			break;
+
+		// balise SPIP
+		default: 
+			list($c,$m) = calculer_champ($objet->fonctions, 
+				$objet->nom_champ,
+				$id_boucle,
+				$boucles,
+				$id_mere);
+			$commentaire = "/* #$objet->nom_champ */";
+			list($bc,$bm) = calculer_liste($objet->cond_avant, $prefix,
+				$id_boucle, $niv+2,$boucles, $id_mere); 
+			list($ac,$am) = calculer_liste($objet->cond_apres, $prefix,
+				$id_boucle, $niv+2,$boucles, $id_mere);
+			$oc = "''";
+			$om = "";
+			$traitement_champ = true;
+			break;
+
+		} // switch
+
+
+		// traitement commun des champs et boucles.
+		// Produit:
+		// m ; if (Tniv+1 = v)
+		// { bm; Tniv+1 = $bc . Tniv+1; am; Tniv+1 .= $ac }
+		// else { om; $Tniv+1 = $oc }
+		// Tniv .= Tniv+1
+		// Optimisations si une au moins des 4 se'quences $*m  est vide
+
+		if ($traitement_champ) {
+			if ($m) {
+				// il faut achever le traitement de l'exp pre'ce'dente
+				if ($exp) {
+					$texte .= "\n		\$t$niv " .
+					(($firstset) ? "=" : ".=") . $exp.';';
+					$firstset = false;
+					$exp = "";
+				}
+				$texte .= "\n$commentaire\n$m//\n";
+			}
+
+			# alternative a la branche ci-dessus ?
+			# if ($m) $c = "eval('".texte_script($m)."').$c";
+			# if ($m) $c = "{$m}.$c"; ne marche pas
+
+			// 3 sequences vides: 'if' inutile
+			if (!($bm || $am || $om)) {
+				if ($exp)
+					$exp .= ' . ';
+				$a = (($bc == "''") ?  "" : "$bc .") .
+				$t .
+				(($ac == "''") ?  "" : " . $ac");
+				// s'il y a un avant ou un apre`s ou un alternant, il faut '?'
+				if (($a != $t) || ($oc != "''"))
+					$exp .= "(($t = $c$commentaire) ?\n	($a) : ($oc))";
+				else
+					$exp .= "$c$commentaire";
+			} else {
+				// il faut achever le traitement de l'exp pre'ce'dente
+				if ($exp) {
+					$texte .= "\n		\$t$niv " .
+					(($firstset) ? "=" : ".=") .
+					"$exp;";
+					$firstset = false;
+				}
+				$exp = (($bc == "''") ? $t : "($bc . $t)");
+				$texte .= "\n		if ($t = $c)\n{" . $bm;
+				if ($am) {
+					$texte .= "$t = $exp;\n	$am";
+					$exp = $t;
+				}
+				if ($ac != "''")
+					$exp = "($exp . $ac)";
+				$texte .= (($exp == $t) ? '' : ("$t = $exp;")) . ";}";
+				if ($om || ($oc != "''"))
+					$texte .= " else { $om$t = ($oc);}";
+				$exp = $t;
+			}
+		}
+	} // foreach
+
+	if (!$exp)
+		$exp ="''";
+
 	return  (!$texte ? array ($exp, "") : 
-		 array(($firstset ? $exp : ('$t'.$niv. ". $exp")),$texte));
+	array(($firstset ? $exp : ('$t'.$niv. ". $exp")),$texte));
 }
 
-# Prend en argument le source d'un squelette, sa grammaire et un nom.
-# Retourne une fonction PHP/SQL portant ce nom et calculant une page HTML.
-# Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
-# - 1er: element 'cache' => nom (du fichier ou` mettre la page)
-# - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
-# Elle retourne alors un tableau de 4 e'le'ments:
-# - 'texte' => page HTML, application du squelette a` l'environnement;
-# - 'squelette' => le nom du squelette
-# - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
-# - 'invalideurs' =>  de'pendances de cette page, pour invalider son cache.
-# (voir son utilisation, optionnelle, dans invalideur.php)
-# En cas d'erreur, elle retourne un tableau des 2 premiers elements seulement
+// Prend en argument le source d'un squelette, sa grammaire et un nom.
+// Retourne une fonction PHP/SQL portant ce nom et calculant une page HTML.
+// Pour appeler la fonction produite, lui fournir 2 tableaux de 1 e'le'ment:
+// - 1er: element 'cache' => nom (du fichier ou` mettre la page)
+// - 2e: element 0 contenant un environnement ('id_article => $id_article, etc)
+// Elle retourne alors un tableau de 4 e'le'ments:
+// - 'texte' => page HTML, application du squelette a` l'environnement;
+// - 'squelette' => le nom du squelette
+// - 'process_ins' => 'html' ou 'php' selon la pre'sence de PHP dynamique
+// - 'invalideurs' =>  de'pendances de cette page, pour invalider son cache.
+// (voir son utilisation, optionnelle, dans invalideur.php)
+// En cas d'erreur, elle retourne un tableau des 2 premiers elements seulement
 
 function calculer_squelette($squelette, $nom, $gram) {
 
-# Phraser le squelette, selon sa grammaire
-# pour le moment: "html" seul connu (HTML+balises BOUCLE)
-  $boucles = '';
-  include_local("inc-$gram-squel.php3");
-  $racine = parser($squelette, '',$boucles);
-#  include_local('inc-debug.php3');
-#  afftable($racine);
-#  affboucles($boucles);
+	// Phraser le squelette, selon sa grammaire
+	// pour le moment: "html" seul connu (HTML+balises BOUCLE)
+	$boucles = '';
+	include_local("inc-$gram-squel.php3");
+	$racine = parser($squelette, '',$boucles);
+	// include_local('inc-debug.php3');
+	// afftable($racine);
+	// affboucles($boucles);
  
-# Commencer par réperer les boucles appelées explicitement par d'autres
-# car elles indexent leurs arguments de manière dérogatoire
-
-  if ($boucles)
-    {
-      foreach($boucles as $id => $boucle)
-	{ 
-	  if ($boucle->type_requete == 'boucle')
-	    {
-	      $rec = &$boucles[$boucle->param];
-	      if (!$rec)
-		{
-		  return array(_T('info_erreur_squelette'),
-			       ($boucle->param . _L('&nbsp: boucle récursive non définie')));
-		  } 
-
-	      $rec->externe = $id;
-	      $boucles[$id]->return =
-		calculer_liste(array($rec),
-			       $nom,
-			       $boucle->param,
-			       1,
-			       $boucles,
-			       $id);
-	    }
+	// Commencer par reperer les boucles appelees explicitement par d'autres
+	// car elles indexent leurs arguments de maniere derogatoire
+
+	if ($boucles) foreach($boucles as $id => $boucle) {
+		if ($boucle->type_requete == 'boucle') {
+			$rec = &$boucles[$boucle->param];
+			if (!$rec) {
+				return array(_T('info_erreur_squelette'),
+				($boucle->param . _L('&nbsp: boucle recursive non definie')));
+			} 
+
+			$rec->externe = $id;
+			$boucles[$id]->return =
+				calculer_liste(array($rec),
+					$nom,
+					$boucle->param,
+					1,
+					$boucles,
+					$id);
+		}
 	} 
-      foreach($boucles as $id => $boucle)
-	{ 
-	  if ($boucle->type_requete != 'boucle') 
-	    {
-	      $res = calculer_params($id, $boucles);
-	      if (is_array($res)) return $res;
-	      $boucles[$id]->return =
-		calculer_liste($boucle->milieu,
-			       $nom,
-			       $id,
-			       1,
-			       $boucles,
-			       $id);
-	    }
-	}
-    }
-
-  // idem pour la racine
-
-  list($return,$corps) =
-    calculer_liste($racine, $nom, '',0, $boucles, '');
-
-  // Corps de toutes les fonctions PHP,
-  // en particulier les requetes SQL et TOTAL_BOUCLE
-  // de'terminables seulement maintenant
-  // Les 3 premiers parame`tres sont passe's par re'fe'rence
-  // (sorte d'environnements a` la Lisp 1.5)
-  // sauf pour la fonction principale qui recoit les initialisations
-
-  $code = '';
 
-  if ($boucles)
-    {
-      foreach($boucles as $id => $boucle)
-	{
-	  $boucles[$id]->return = calculer_boucle($id, $boucles); 
+	if ($boucles) foreach($boucles as $id => $boucle) { 
+		if ($boucle->type_requete != 'boucle') {
+			$res = calculer_params($id, $boucles);
+
+			// C'est quoi ca ???
+			if (is_array($res))
+				return $res;
+
+			$boucles[$id]->return = calculer_liste($boucle->milieu,
+				$nom,
+				$id,
+				1,
+				$boucles,
+				$id);
+		}
 	}
-      
-      foreach($boucles as $id => $boucle) 
-	{
-	  $code .= "\n\n// BOUCLE".$id.
-	  "\nfunction $nom" . ereg_replace("-","_",$id) .
-		'(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
-	    $boucle->return .
-	    "\n}\n";
+
+	// idem pour la racine
+	list($return,$corps) = calculer_liste($racine, $nom, '',0, $boucles, '');
+
+
+	// Corps de toutes les fonctions PHP,
+	// en particulier les requetes SQL et TOTAL_BOUCLE
+	// de'terminables seulement maintenant
+	// Les 3 premiers parame`tres sont passe's par re'fe'rence
+	// (sorte d'environnements a` la Lisp 1.5)
+	// sauf pour la fonction principale qui recoit les initialisations
+
+	$code = '';
+	if ($boucles) {
+		foreach($boucles as $id => $boucle)
+			$boucles[$id]->return = calculer_boucle($id, $boucles); 
+
+		foreach($boucles as $id => $boucle) {
+
+			// Reproduire la boucle en commentaire
+			$pretty = "BOUCLE$id(".strtoupper($boucle->type_requete).")";
+			if (is_array($boucle->param))
+				$pretty .= " {".join("} {", $boucle->param)."}";
+			$pretty = ereg_replace("[\r\n]", " ", $pretty);
+
+			// Puis envoyer son code
+			$code .= "\n//\n// <$pretty>\n//\n"
+			."function $nom" . ereg_replace("-","_",$id) .
+			'(&$Cache, &$Pile, &$doublons, &$Numrows, $SP) {' .
+			$boucle->return .
+			"\n}\n\n";
+		}
 	}
-    }
-  return $code . '
+
+	return $code . '
+//
 // Fonction principale du squelette
+//
 function ' . $nom . '($Cache, $Pile, $doublons, $Numrows="", $SP=0) {
-' .
-    $corps . "\n \$t0 = " . $return . ';
-    return array("texte" => $t0,
+' . $corps . "\n	\$t0 = " . $return . ';
+	return array("texte" => $t0,
 		"squelette" => "' . $nom . '",
 		"process_ins" => ((strpos($t0,\'<\'.\'?\')=== false) ? \'html\' : \'php\'),
-		"invalideurs" => $Cache);' .
-    "\n}\n" ;
+		"invalideurs" => $Cache);' . "\n}\n" ;
+
 }
+
 ?>
diff --git a/inc-calcul.php3 b/inc-calcul.php3
index 94b70974e6f0a4bf65aa7502a5ec6f0c682fb179..91bd8fd195ddbae507f85c0fec696ebd351e87cd 100644
--- a/inc-calcul.php3
+++ b/inc-calcul.php3
@@ -14,6 +14,7 @@ include_ecrire("inc_texte.php3");
 include_ecrire("inc_filtres.php3");
 include_ecrire("inc_lang.php3");
 include_ecrire("inc_documents.php3");
+include_ecrire("inc_forum.php3");
 include_local("inc-calcul_mysql3.php");
 include_local("inc-calcul_html4.php");
 
diff --git a/inc-index-squel.php3 b/inc-index-squel.php3
index 47476b2e65ed8c97b5b763f013abfcc4ed3eb48b..f08b187e68d8e86afe873d729f5a4f79af78afd7 100644
--- a/inc-index-squel.php3
+++ b/inc-index-squel.php3
@@ -83,28 +83,32 @@ function index_pile($idb, $nom_champ, &$boucles) {
 # on lui passe la main et elle est cense retourner le tableau ci-dessus
 # (Essayer de renvoyer une suite vide, ca diminue les allocations a l'exec)
 
-
+// cette fonction sert d'API pour demander le champ '$champ' dans la pile
+function champ_sql($champ, $p) {
+	return index_pile($p->id_boucle, $champ, $p->boucles);
+}
 
 function calculer_champ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere) {
 	// Preparer les parametres
-	$params = new ParamChamp;
-	$params->fonctions = $fonctions;
-	$params->nom_champ = $nom_champ;
-	$params->id_boucle = $id_boucle;
-	$params->boucles = $boucles;
-	$params->id_mere = $id_mere;
-
-	// regarder s'il existe une fonction perso pour #NOM
-	$f = 'perso_' . $nom_champ;
-	if (function_exists($f))
-		return $f($params);
-
-	// regarder s'il existe une fonction new style pour #NOM
-	$f = 'calculer_balise_' . $nom_champ;
-	if (function_exists($f)) 
-		return $f($params);
-
-	// regarder s'il existe une fonction old style pour #NOM
+	$p = new ParamChamp;
+	$p->fonctions = $fonctions;
+	$p->nom_champ = $nom_champ;
+	$p->id_boucle = $id_boucle;
+	$p->boucles = $boucles;
+	$p->id_mere = $id_mere;
+
+	// regarder s'il existe une fonction personnalisee balise_NOM()
+	$f = 'balise_' . $nom_champ;
+	if (function_exists($f) AND $p = $f($p))
+		return $p->retour();
+
+	// regarder s'il existe une fonction standard balise_NOM_dist()
+	$f = 'balise_' . $nom_champ . '_dist';
+	if (function_exists($f) AND $p = $f($p))
+		return $p->retour();
+
+	# A SUPPRIMER
+	// regarder s'il existe une fonction old style calculer_champ_NOM()
 	$f = 'calculer_champ_' . $nom_champ;
 	if (function_exists($f)) 
 		return $f($fonctions, $nom_champ, $id_boucle, $boucles, $id_mere);
diff --git a/inc-logo-squel.php3 b/inc-logo-squel.php3
index 0558b05046567e5763bf9d9d8ef74019131a7de6..a14df8d5e3d2f41b4fc5506a51a70b57a79a2f82 100644
--- a/inc-logo-squel.php3
+++ b/inc-logo-squel.php3
@@ -1,71 +1,101 @@
 <?php
 
+
 # Fonctions de traitement de champs Spip homonymes de champs SQL
 # mais non e'quivalent
 
-function calculer_champ_EXTRA ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere)
-{
-  $code = 'trim(' . index_pile($id_boucle,  "extra", $boucles) . ')';
-  if ($fonctions) {
+
+// #EXTRA [(#EXTRA|isbn)]
+// Champs extra
+// Non documentes, en voie d'obsolescence, cf. ecrire/inc_extra.php3
+function balise_EXTRA_dist ($p) {
+	$_extra = champ_sql('extra', $p);
+	$p->code = 'trim($_extra)';
+
     // Gerer la notation [(#EXTRA|isbn)]
-    include_ecrire("inc_extra.php3");
-    reset($fonctions);
-    list($key, $champ_extra) = each($fonctions);
-    $type_extra = $boucles[$id_boucle]->type_requete;
-    if (extra_champ_valide($type_extra, $champ_extra)) {
-      unset($fonctions[$key]);
-      $code = "extra($code, '".addslashes($champ_extra  )."')";
-    }
-    // Appliquer les filtres definis par le webmestre
-    $filtres = extra_filtres($type_extra, $champ_extra);
-    if ($filtres) {
-      reset($filtres);
-      while (list(, $f) = each($filtres)) $code = "$f($code)";
-    }
-  }
-  return applique_filtres($fonctions, $code, $id_boucle, $boucles, $id_mere);
+	if ($p->fonctions) {
+		include_ecrire("inc_extra.php3");
+		foreach ($p->fonctions as $key => $champ_extra)
+			$type_extra = $p->boucles[$p->id_boucle]->type_requete;
+			// ci-dessus est sans doute un peu buggue : si on invoque #EXTRA
+			// depuis un sous-objet sans champ extra d'un objet a champ extra,
+			// on aura le type_extra du sous-objet (!)
+		if (extra_champ_valide($type_extra, $champ_extra)) {
+			unset($p->fonctions[$key]);
+			$p->code = "extra($p->code, '".addslashes($champ_extra)."')";
+		}
+		// Appliquer les filtres definis par le webmestre
+		$filtres = extra_filtres($type_extra, $champ_extra);
+		if ($filtres) foreach ($filtres as $f)
+			$p->code = "$f($p->code)";
+	}
+	return $p;
 }
 
-function calculer_champ_LANG ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere)
- {
-	$code = '(($x = '.index_pile($id_boucle,  "lang", $boucles).') ? $x : $GLOBALS[spip_lang])';
-   return applique_filtres($fonctions, $code, $id_boucle, $boucles, $id_mere);
+
+// #LANG
+// non documente ?
+function balise_LANG_dist ($p) {
+	$_lang = champ_sql('lang', $p);
+	$p->code = '($_lang ? $_lang : $GLOBALS[spip_lang])';
+	return $p;
 }
 
-function calculer_champ_LESAUTEURS ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere)
-{
-  $code = index_pile($id_boucle, 'lesauteurs', $boucles);
-  if ((!$code) || ($code == '$Pile[0][lesauteurs]'))
-    $code = 'sql_auteurs(' .
-      index_pile($id_boucle,  "id_article", $boucles) .
-      ')';    
-   return applique_filtres($fonctions, $code, $id_boucle, $boucles, $id_mere);
+
+// #LESAUTEURS
+// les auteurs d'un article (ou d'un article syndique)
+// http://www.spip.net/fr_article902.html
+// http://www.spip.net/fr_article911.html
+function balise_LESAUTEURS_dist ($p) {
+	// Cherche le champ 'lesauteurs' dans la pile
+	$_lesauteurs = champ_sql('lesauteurs', $p);
+
+	// Si le champ n'existe pas (cas de spip_articles), on donne la
+	// construction speciale sql_auteurs(id_article) ;
+	// dans le cas contraire on prend le champ 'les_auteurs' (cas de
+	// spip_syndic_articles)
+	if ($_lesauteurs AND $_lesauteurs != '$Pile[0][lesauteurs]') {
+		$p->code = $_lesauteurs;
+	} else {
+		$_id_article = champ_sql('id_article', $p);
+		$p->code = "sql_auteurs($_id_article)";
+	}
+
+	return $p;
 }
 
-function calculer_champ_PETITION ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere)
- {
-   $code = 'sql_petitions(' .
-     index_pile($id_boucle,  'id_article', $boucles)
-     . '")) ? " " : "")';
-  return applique_filtres($fonctions, $code, $id_boucle, $boucles, $id_mere);
+
+// #PETITION
+// Champ testant la presence d'une petition
+// non documente ???
+function balise_PETITION_dist ($p) {
+	$_id_article = champ_sql('id_article', $p);
+	$p->code = 'sql_petitions($_id_article)';
+	return $p;
 }
 
-function calculer_champ_POPULARITE ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere)
- {
-   $code = 'ceil(min(100, 100 * ' .
-     index_pile($id_boucle,  "popularite", $boucles) .
-     '/ max(1 , 0 + lire_meta(\'popularite_max\'))))';
-  return applique_filtres($fonctions, $code, $id_boucle, $boucles, $id_mere);
- }
+
+// #POPULARITE
+// http://www.spip.net/fr_article1846.html
+function balise_POPULARITE_dist ($p) {
+	$_popularite = champ_sql('popularite', $p);
+	$p->code = "ceil(min(100, 100 * $_popularite
+	/ max(1 , 0 + lire_meta('popularite_max'))))";
+	return $p;
+}
 
 
-function calculer_champ_DATE ($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere) {
-# Uniquement hors-boucles, pour date passee dans l'URL ou  contexte_inclus
-  return applique_filtres($fonctions,
-			  index_pile($id_boucle,  'date', $boucles),
-			  $id_boucle, $boucles, $id_mere);
+// #DATE
+// Cette fonction n'est utile que parce qu'on a besoin d'aller chercher
+// dans le contexte general quand #DATE est en dehors des boucles
+// http://www.spip.net/fr_article1971.html
+function balise_DATE_dist ($p) {
+	$_date = champ_sql('date', $p);
+	$p->code = "$_date";
+	return $p;
 }
 
+
 # Fonction commune aux logos (rubriques, articles...)
 
 function calculer_champ_LOGO($fonctions, $nom_champ, $id_boucle, &$boucles, $id_mere)
diff --git a/inc-reqsql-squel.php3 b/inc-reqsql-squel.php3
index 0135956b52648d5635f252df551622e244f87f23..364453092a51f21e1ccc4087cb2ff83d15d91c79 100644
--- a/inc-reqsql-squel.php3
+++ b/inc-reqsql-squel.php3
@@ -89,7 +89,6 @@ function calculer_requete(&$boucle) {
     
   case 'hierarchie':
     $boucle->from[] =  "rubriques AS $id_table";
-#    $boucle->select[] = "rubriques.id_parent";
     break;
     
   case 'syndication':
@@ -115,9 +114,9 @@ function calculer_requete(&$boucle) {
 	// En absence de champ c'est un decompte : on prend la primary pour
 	// avoir qqch (le marteau-pilon * est trop couteux, et le COUNT
 	// incompatible avec le cas general)
-	return 'spip_abstract_select(array("'. 
+	return "spip_abstract_select(\n\t\tarray(\"". 
 		((!$boucle->select) ? $id_field :
-		join('", "', array_unique($boucle->select))) .
+		join("\",\n\t\t\"", array_unique($boucle->select))) .
 		'"), # SELECT
 		array("' .
 		join('","', array_unique($boucle->from)) .
diff --git a/inc-vrac-squel.php3 b/inc-vrac-squel.php3
index 98e3aecbb9269f7f0255046ec6f10c79250d4795..36f236d4dd09a5523c153b8c3e5f50bb1f9674a5 100644
--- a/inc-vrac-squel.php3
+++ b/inc-vrac-squel.php3
@@ -293,14 +293,14 @@ function calculer_champ_divers($fonctions, $nom_champ, $id_boucle, &$boucles, $i
 // Fonctions OK
 //
 
-function calculer_balise_INTRODUCTION($params) {
-	$params->code = 'calcul_introduction(\'' .
-		$params->boucles[$params->id_boucle]->type_requete . "',\n" .
-		index_pile($params->id_boucle, "texte", $params->boucles) . ",\n" .
-		index_pile($params->id_boucle, "chapo", $params->boucles) . ",\n" .
-		index_pile($params->id_boucle, "descriptif", $params->boucles) . ")\n"; 
-
-	return $params->retour();
+function balise_INTRODUCTION_dist ($p) {
+	$_type = $p->boucles[$p->id_boucle]->type_requete;
+	$_texte = champ_sql('texte', $p);
+	$_chapo = champ_sql('chapo', $p);
+	$_descriptif = champ_sql('descriptif', $p);
+	$p->code = "calcul_introduction('$_type', $_texte, $_chapo, $_descriptif)";
+
+	return $p;
 }
 
 ?>