diff --git a/ecrire/inc_version.php3 b/ecrire/inc_version.php3
index 00c51735653476625c6f46e3bebe32a629521e5f..f70f0f2030431c4ec433dc629ce48a497342d487 100644
--- a/ecrire/inc_version.php3
+++ b/ecrire/inc_version.php3
@@ -412,7 +412,7 @@ define_once('_AUTH_USER_FILE', '.htpasswd');
 $spip_version = 1.816;
 
 // version de spip
-$spip_version_affichee = "1.8.1 CVS";
+$spip_version_affichee = "1.8.2 CVS alpha";
 
 // version de spip / tag cvs
 if (ereg('Name: v(.*) ','$Name$', $regs)) $spip_version_affichee = $regs[1];
diff --git a/inc-balises.php3 b/inc-balises.php3
index bab720047465c073fb739e647ed6ff11c3389caa..d0854c826b70621686bb9b4343f086dac63b0bfd 100644
--- a/inc-balises.php3
+++ b/inc-balises.php3
@@ -372,7 +372,7 @@ function balise_EXPOSER_dist($p) {
 		$on = addslashes($regs[1]);
 		$off = addslashes($regs[3]);
 		// autres filtres
-		array_shift($p->args);
+		array_shift($p->param);
 	}
 
 
@@ -393,7 +393,7 @@ function balise_EMBED_DOCUMENT_dist($p) {
 	$p->code = "calcule_embed_document(intval($_id_document), " .
 	  argumenter_balise($p->fonctions, "|") .
 	  ", \$doublons, '" . $p->descr['documents'] . "')";
-	$p->args = array();
+	$p->param = array();
 	$p->statut = 'html';
 	return $p;
 }
@@ -544,7 +544,7 @@ function calculer_balise_logo ($p) {
 		foreach($p->fonctions as $couple) {
 			// eliminer les faux filtres
 			if (!$flag_stop) {
-				array_shift($p->args);
+				array_shift($p->param);
 				$nom = $couple[0];
 				if (ereg('^(left|right|center|top|bottom)$', $nom))
 					$align = $nom;
@@ -635,7 +635,7 @@ function balise_EXTRA_dist ($p) {
 
 	// Gerer la notation [(#EXTRA|isbn)]
 	if ($p->fonctions) {
-	  $p->args = array();
+	  $p->param = array();
 		include_ecrire("inc_extra.php3");
 		list ($key, $champ_extra) = each($p->fonctions);	// le premier filtre
 		$type_extra = $p->type_requete;
@@ -699,11 +699,11 @@ function balise_PARAMETRES_FORUM_dist($p) {
 	// Syntaxe [(#PARAMETRES_FORUM{#SELF})] pour fixer le retour du forum
 	# note : ce bloc qui sert a recuperer des arguments calcules pourrait
 	# porter un nom et faire partie de l'API.
-	if ($filtres = $p->args) {
+	if ($filtres = $p->param) {
 		$retour = array_shift($filtres);
 		if  (!array_shift($retour)) {
 		  $p->fonctions = $a;
-		  array_shift( $p->args );
+		  array_shift( $p->param );
 		  $retour = calculer_liste($retour[0],
 					   $p->descr,
 					   $p->boucles,
@@ -761,11 +761,11 @@ function balise_SELF_dist($p) {
 //
 function balise_ENV_dist($p) {
 
-	if ($a = $p->args) {
+	if ($a = $p->param) {
 		$sinon = array_shift($a);
 		if  (!array_shift($sinon)) {
 		  $p->fonctions = $a;
-		  array_shift( $p->args );
+		  array_shift( $p->param );
 		  $nom = array_shift($sinon);
 		  $nom = ($nom[0]->type=='texte') ? $nom[0]->texte : "";
 		}
diff --git a/inc-compilo-api.php3 b/inc-compilo-api.php3
index 6c958acdf3fe62f5b7c83d448274beb0fde48e91..9025e09a711960d8fd2c541a691840a012d0c24d 100644
--- a/inc-compilo-api.php3
+++ b/inc-compilo-api.php3
@@ -21,12 +21,13 @@ define("_INC_COMPILO_API", "1");
 class Texte {
 	var $type = 'texte';
 	var $texte;
+	var $avant, $apres = ""; // s'il y avait des guillemets autour
 }
 
 class Inclure {
 	var $type = 'include';
 	var $texte;
-	var $args = array();  //  valeurs des params
+	var $param = array();  //  valeurs des params
 	var $avant, $apres; // inutilises mais generiques
 }
 
@@ -73,10 +74,9 @@ class Boucle {
 // sous-noeud du precedent
 
 class Critere {
-	var $operateur;
-	var $arg1;
-	var $arg2;
+	var $op;
 	var $not;	
+	var $param;
 }
 
 class Champ {
@@ -85,7 +85,7 @@ class Champ {
 	var $nom_boucle= ''; // seulement si boucle explicite
 	var $avant, $apres; // tableaux d'objets
 	var $etoile;
-	var $filtres = array();  // filtre explicites
+	var $param = array();  // filtre explicites
 	var $fonctions = array();  // source des filtres (compatibilite)
 	// champs pour la production de code
 	var $id_boucle;
@@ -106,8 +106,8 @@ class Idiome {
 	var $type = 'idiome';
 	var $nom_champ = ""; // la chaine a traduire
 	var $module = ""; // son module de definition
-	var $filtres = array(); // les filtres a appliquer au resultat
-	var $fonctions = array(); // source des filtres
+	var $param = array(); // les filtres a appliquer au resultat
+	var $fonctions = array(); // source des filtres  (compatibilite)
 	var $avant, $apres; // inutilises mais faut = ci-dessus
 	// champs pour la production de code, cf ci-dessus
 	var $id_boucle;
diff --git a/inc-compilo-index.php3 b/inc-compilo-index.php3
index 0b82016f4653fd905587b795ccbb59a070dfddbf..811adc3d06c296991cee8d92fdc17ecc9a82cd4c 100644
--- a/inc-compilo-index.php3
+++ b/inc-compilo-index.php3
@@ -192,11 +192,11 @@ function calculer_balise($nom, $p) {
 function calculer_balise_dynamique($p, $nom, $l) {
 	balise_distante_interdite($p);
 	$param = "";
-	if ($a = $p->args) {
+	if ($a = $p->param) {
 		$c = array_shift($a);
 		if  (!array_shift($c)) {
 		  $p->fonctions = $a;
-		  array_shift( $p->args );
+		  array_shift( $p->param );
 		  $param = compose_filtres_args($p, $c, ',');
 		}
 	}
@@ -205,11 +205,11 @@ function calculer_balise_dynamique($p, $nom, $l) {
 	  . $collecte
 	  . ($collecte ? $param : substr($param,1)) # virer la virgule
 	  . "),\n\tarray("
-	  . argumenter_balise($p->args, "', '")
+	  . argumenter_balise($p->param, "', '")
 	  . "), \$GLOBALS['spip_lang'])";
 	$p->statut = 'php';
 	$p->fonctions = array();
-	$p->args = array();
+	$p->param = array();
 
 	// Cas particulier de #FORMULAIRE_FORUM : inserer l'invalideur
 	if ($nom == 'FORMULAIRE_FORUM')
@@ -218,15 +218,6 @@ function calculer_balise_dynamique($p, $nom, $l) {
 	return $p;
 }
 
-function param_balise(&$p) {
-  if (!($a=$p->fonctions)) return "";
-  $c = array_shift($a);
-  if  ($c[0]) return "";
-  $p->fonctions = $a;
-  array_shift( $p->args );
-  return $c[1];
-}
-
 // construire un tableau des valeurs interessant un formulaire
 
 function collecter_balise_dynamique($l, $p) {
@@ -253,7 +244,7 @@ function applique_filtres($p) {
 //  processeurs standards (cf inc-balises.php3)
 	$code = ($p->etoile ? $p->code : champs_traitements($p));
 	// Appliquer les filtres perso
-	if ($p->args) $code = compose_filtres($p, $code);
+	if ($p->param) $code = compose_filtres($p, $code);
 	// post-traitement securite
 	if ($p->statut == 'html') $code = "interdire_scripts($code)";
 	return $code;
@@ -261,7 +252,7 @@ function applique_filtres($p) {
 
 function compose_filtres($p, $code)
 {
-  foreach($p->args as $filtre) {
+  foreach($p->param as $filtre) {
     $fonc = array_shift($filtre);
     if ($fonc) {
       $arglist = compose_filtres_args($p, $filtre, ($fonc == '?' ? ':' : ','));
diff --git a/inc-compilo.php3 b/inc-compilo.php3
index bbbb3e7a0964489e62b211f4ad7fd2c4180c9edc..f1496d2d700d4c60135a6f5c15e7ec1aa2840b0c 100644
--- a/inc-compilo.php3
+++ b/inc-compilo.php3
@@ -58,7 +58,7 @@ function calculer_inclure($struct, $descr, &$boucles, $id_boucle, $niv) {
 	}
 
 	$l = array();
-	foreach($struct->args as $val) {
+	foreach($struct->param as $val) {
 		$var = array_shift($val);
 		$l[] = "\'$var\' => \'' . addslashes(" .
 		    ($val ? calculer_liste($val[0], $descr, $boucles, $id_boucle, $niv) :
@@ -513,27 +513,21 @@ function calculer_squelette($squelette, $nom, $gram, $sourcefile) {
 
 	if ($boucles) {
 	  // une boucle documents est conditionnee par tout le reste!
-	  // une boucle avec critere de recheche conditionne tout le reste!
-	  // (a cause du cas #nom_de_boucle:URL_*)
-	  // 
-		foreach($boucles as $idb => $boucle)
-		  {
-		    if ($boucle->param && is_array($boucle->param)) {
+		foreach($boucles as $idb => $boucle) {
+			if ($boucle->param) {
 				if (($boucle->type_requete == 'documents') && 
-				    in_array('doublons',$boucle->param))
+				     $boucle->doublons)
 				  { $descr['documents'] = true; break; }
-				if (in_array('recherche',$boucle->param))
-					$boucles[$idb]->hash = true;
 			}
 		  }
 	// Commencer par reperer les boucles appelees explicitement 
 	// car elles indexent les arguments de maniere derogatoire
 		foreach($boucles as $id => $boucle) { 
 			if ($boucle->type_requete == 'boucle') {
-				$rec = &$boucles[$boucle->param];
+				$rec = &$boucles[$boucle->param[0]];
 				if (!$rec) {
 					return array(_T('zbug_info_erreur_squelette'),
-						($boucle->param
+						($boucle->param[0]
 						. ' '. _T('zbug_boucle_recursive_undef')));
 				} else {
 					$rec->externe = $id;
@@ -602,8 +596,9 @@ function calculer_squelette($squelette, $nom, $gram, $sourcefile) {
 
 			// Reproduire la boucle en commentaire
 			$pretty = "BOUCLE$id(".strtoupper($boucle->type_requete).")";
-		    if ($boucle->param && is_array($boucle->param)) 
-				$pretty .= " {".join("} {", $boucle->param)."}";
+			// anachronique. A refaire.
+			/*    if ($boucle->param && is_array($boucle->param)) 
+			 $pretty .= " {".join("} {", $boucle->param)."}";*/
 			// sans oublier les parametres traites en amont
 		    if ($boucle->separateur)
 		      foreach($boucle->separateur as $v)
diff --git a/inc-criteres.php3 b/inc-criteres.php3
index 85a4b53a698508e5bf72df6e05b8fc91b31732ea..c953443fbe0ecb21dc2deb3da110ef8cd7b7dd06 100644
--- a/inc-criteres.php3
+++ b/inc-criteres.php3
@@ -22,10 +22,11 @@ define("_INC_CRITERES", "1");
 
 // {racine}
 // http://www.spip.net/@racine
-function critere_racine_dist($idb, &$boucles, $param, $not) {
+function critere_racine_dist($idb, &$boucles, $crit) {
+	$not = $crit->not;
 	$boucle = &$boucles[$idb];
 
-	if ($param != 'racine' OR $not)
+	if ($not)
 		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
 
 	$boucle->where[] = $boucle->id_table.".id_parent='0'";
@@ -34,28 +35,24 @@ function critere_racine_dist($idb, &$boucles, $param, $not) {
 
 // {exclus}
 // http://www.spip.net/@exclus
-function critere_exclus_dist($idb, &$boucles, $param, $not) {
+function critere_exclus_dist($idb, &$boucles, $crit) {
+	$param = $crit->op;
+	$not = $crit->not;
 	$boucle = &$boucles[$idb];
 	$id = $boucle->primary;
 
-	if ($param != 'exclus' OR $not OR !$id)
+	if ($not OR !$id)
 		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
 
 	$arg = calculer_argument_precedent($idb,$id, $boucles);
 	$boucle->where[] = $boucle->id_table . '.' . $id."!='\"." . $arg . ".\"'";
-
 }
 
 // {doublons} ou {unique}
 // http://www.spip.net/@doublons
-function critere_doublons_dist($idb, &$boucles, $param, $not) {
+function critere_doublons_dist($idb, &$boucles, $crit) {
 	$boucle = &$boucles[$idb];
-
-	if (!preg_match("/(doublons|unique)[[:space:]]*([a-z_0-9]*)/i",
-	$param, $match))
-		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
-
-	$boucle->doublons = $boucle->type_requete . $match[2];
+	$boucle->doublons = $boucle->type_requete . $crit->param[0][0]->texte;
 	$boucle->where[] = '" .' .
 		"calcul_mysql_in('".$boucle->id_table . '.' . $boucle->primary."', "
 		.'"0".$doublons[\''.$boucle->doublons."'], 'NOT') . \"";
@@ -63,33 +60,28 @@ function critere_doublons_dist($idb, &$boucles, $param, $not) {
 
 // {lang_select}
 // http://www.spip.net/@lang_select
-function critere_lang_select_dist($idb, &$boucles, $param, $not) {
+function critere_lang_select_dist($idb, &$boucles, $crit) {
+	if (!($param = $crit->param[1][0]->texte)) $param = 'oui';
+	if ($crit->not)	$param = ($param=='oui')?'non':'oui';
 	$boucle = &$boucles[$idb];
-	if (preg_match('/lang_select(=(oui|non))?$/i', $param, $match)) {
-		if (!$lang_select = $match[2])
-			$lang_select = 'oui';
-		if ($not)
-			$lang_select = ($lang_select=='oui')?'non':'oui';
-		$boucle->lang_select = $lang_select;
-	}
-	else erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
+	$boucle->lang_select = $param;
 }
 
 // {debut_xxx}
 // http://www.spip.net/@debut_
-function critere_debut_dist($idb, &$boucles, $param, $not) {
+function critere_debut_dist($idb, &$boucles, $crit) {
 	$boucle = &$boucles[$idb];
-	if (ereg('^debut([-_a-zA-Z0-9]+),([0-9]*)$', $param, $match)) {
-		$debut_lim = "debut".$match[1];
-		$boucle->limit =
-			'intval($GLOBALS["'.$debut_lim.'"]).",'.$match[2] .'"' ;
-	}
-	else erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
+	$boucle->limit = 'intval($GLOBALS["debut' .
+	  $crit->param[0][0]->texte .
+	  '"]) . ",' .
+	  $crit->param[1][0]->texte .
+	  '"' ;
 }
 
 // {recherche}
 // http://www.spip.net/@recherche
-function critere_recherche_dist($idb, &$boucles, $param, $not) {
+function critere_recherche_dist($idb, &$boucles, $crit) {
+
 	$boucle = &$boucles[$idb];
 
 	$table = $boucle->id_table;	#articles
@@ -108,10 +100,13 @@ function critere_recherche_dist($idb, &$boucles, $param, $not) {
 
 // {inverse}
 // http://www.spip.net/@inverse
-function critere_inverse_dist($idb, &$boucles, $param, $not) {
+function critere_inverse_dist($idb, &$boucles, $crit) {
+	$param = $crit->op;
+	$not = $crit->not;
 	$boucle = &$boucles[$idb];
 	// Classement par ordre inverse
-	if ($param == 'inverse' AND !$not) {
+
+	if (!$not) {
 		if ($boucle->order)
 			$boucle->order .= ".' DESC'";
 		else 
@@ -125,10 +120,9 @@ function critere_inverse_dist($idb, &$boucles, $param, $not) {
 // http://www.spip.net/@traduction
 //   (id_trad>0 AND id_trad=id_trad(precedent))
 //    OR id_article=id_article(precedent)
-function critere_traduction_dist($idb, &$boucles, $param, $not) {
+function critere_traduction_dist($idb, &$boucles, $crit) {
 	$boucle = &$boucles[$idb];
-	if ($param == 'traduction') {
-		$boucle->where[] = "((".$boucle->id_table.".id_trad > 0 AND "
+	$boucle->where[] = "((".$boucle->id_table.".id_trad > 0 AND "
 			. $boucle->id_table.".id_trad ='\"."
 			. calculer_argument_precedent($idb, 'id_trad',
 				$boucles)
@@ -138,30 +132,22 @@ function critere_traduction_dist($idb, &$boucles, $param, $not) {
 			. calculer_argument_precedent($idb, $boucle->primary,
 				$boucles)
 			. ".\"'))";
-	} else
-		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
 }
 
 // {origine_traduction}
 // http://www.spip.net/@origine_traduction
-function critere_origine_traduction_dist($idb, &$boucles, $param, $not) {
+function critere_origine_traduction_dist($idb, &$boucles, $crit) {
 	$boucle = &$boucles[$idb];
-	if ($param == 'origine_traduction')
-		$boucle->where[] = $boucle->id_table.".id_trad = "
-		. $boucle->id_table . '.' . $boucle->primary;
-	else
-		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
+	$boucle->where[] = $boucle->id_table.".id_trad = "
+	  . $boucle->id_table . '.' . $boucle->primary;
 }
 
 
 // {meme_parent}
 // http://www.spip.net/@meme_parent
-function critere_meme_parent_dist($idb, &$boucles, $param, $not) {
+function critere_meme_parent_dist($idb, &$boucles, $crit) {
 	$boucle = &$boucles[$idb];
-	if ($param != 'meme_parent')
-		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
-	else {
-		if ($boucle->type_requete == 'rubriques') {
+	if ($boucle->type_requete == 'rubriques') {
 			$boucle->where[] = $boucle->id_table.".id_parent='\"."
 			. calculer_argument_precedent($idb, 'id_parent',
 			$boucles)
@@ -173,49 +159,48 @@ function critere_meme_parent_dist($idb, &$boucles, $param, $not) {
 			. ".\"'";
 			$boucle->where[] = $boucle->id_table.".id_parent > 0";
 			$boucle->plat = true;
-		} else
-			erreur_squelette(_T('zbug_erreur_meme_parent'), "BOUCLE" . $idb);
 	}
 }
 
 // {branche ?}
 // http://www.spip.net/@branche
-function critere_branche_dist($idb, &$boucles, $param, $not) {
+function critere_branche_dist($idb, &$boucles, $crit) {
+	$not = $crit->not;
 	$boucle = &$boucles[$idb];
-	if (preg_match('/branche[[:space:]]*([?])?$/i', $param, $regs)) {
-		$c = "calcul_mysql_in('".$boucle->id_table.".id_rubrique',
+	$c = "calcul_mysql_in('".$boucle->id_table.".id_rubrique',
 		calcul_branche(" . calculer_argument_precedent($idb,
 		'id_rubrique', $boucles) . "), '')";
-		if (!$regs[1])
+	if (!$crit->cond)
 			$where = "\". $c .\"" ;
-		else
+	else
 			$where = "\".("
 			. calculer_argument_precedent($idb, 'id_rubrique',
 			$boucles)."? $c : 1).\"";
 
-		if ($not)
+	if ($not)
 			$boucle->where[] = "NOT($where)";
-		else
+	else
 			$boucle->where[] = $where;
-	} else
-		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
 }
 
 // Tri : {par xxxx}
 // http://www.spip.net/@par
-function critere_par_dist($idb, &$boucles, $param, $not) {
+function critere_par_dist($idb, &$boucles, $crit) {
+
+	$not = $crit->not;
 	$boucle = &$boucles[$idb];
 	if ($not)
 		erreur_squelette(_T('zbug_info_erreur_squelette'), $param);
 
-	$param = ltrim(substr($param,3));
+	$params = $crit->param;
 
-	while ($param) {
-
-		preg_match('/(([^{,]*[{][^}]*[}])|([^,]*))[[:space:]]*,?[[:space:]]*(.*)/ims',
-			   $param, $regs);
-		$param = $regs[4];
-		$tri = $regs[1];
+	foreach ($params as $tri) {
+	// tris specifies dynamiquement
+	    if ($tri[0]->type != 'texte')
+	      $order = calculer_liste($tri, array(), $boucles, $idb);
+	    else {
+	      // on considere que {par   col#ENV{num}} est imposible
+	      $tri = $tri[0]->texte;
 	// par hasard
 		if ($tri == 'hasard') {
 		// tester si cette version de MySQL accepte la commande RAND()
@@ -244,12 +229,13 @@ function critere_par_dist($idb, &$boucles, $param, $not) {
 		  $order = "'num'";
 	}
 	// par champ. Verifier qu'ils sont presents.
-		else if (ereg("^[a-z][a-z0-9]*$", $tri)) {
-		  if ($tri == 'date')
-		    $order = "'".$boucle->id_table.".".
-		      $GLOBALS['table_date'][$boucle->type_requete]
-		      ."'";
-		  else {
+		else
+		  if (ereg("^[a-z][a-z0-9_]*$", $tri)) {
+		    if ($tri == 'date')
+		      $order = "'".$boucle->id_table.".".
+			$GLOBALS['table_date'][$boucle->type_requete]
+			."'";
+		    else {
 			global $table_des_tables, $tables_des_serveurs_sql;
 			$r = $boucle->type_requete;
 			$s = $boucles[$idb]->sql_serveur;
@@ -266,39 +252,24 @@ function critere_par_dist($idb, &$boucles, $param, $not) {
 			}
 		  }
 		}
-	// tris specifies par l'URL ?
-		else {
-		  $order = simplifie_param_dynamique($tri, $boucles, $idb);
-		  if ($order == $tri) $order = "'" . $order . "'";
-		}
+	    }
 	// au final, gestion des tris multiples
 		if ($order) {
 		  if ($boucle->order) $boucle->order .= '.",".';
 		  $boucle->order .= $order;
 		}
-	}
-}
-
-// Le critere {statut=...}
-// particularite : si on l'invoque dans une boucle il faut interdire
-// a la boucle de mettre ses propres criteres de statut
-// http://www.spip.net/@statut (a documenter)
-function critere_statut_dist($idb, &$boucles, $param, $not) {
-	// interdire a function boucle_BOUCLE() de regler le statut
-	$boucle = &$boucles[$idb];
-	$boucle->where['statut'] = '1';
-
-	return calculer_critere_DEFAUT($idb, $boucles, $param, $not);
+	  }
 }
 
-function calculer_critere_parties($idb, &$boucles, $param, $not, $match) {
+function calculer_critere_parties($idb, &$boucles, $crit) {
 	$boucle = &$boucles[$idb];
-	list(,$a1,$op,$a2) = $match;
+	$a1 = $crit->param[0];
+	$a2 = $crit->param[1];
+	$op = $crit->op;
 	list($a11,$a12) = calculer_critere_parties_aux($idb, $boucles, $a1);
 	list($a21,$a22) = calculer_critere_parties_aux($idb, $boucles, $a2);
-
 	if (($op== ',')&&(is_numeric($a11) && (is_numeric($a21))))
-		$boucle->limit = $a11 .',' . $a21;
+	    $boucle->limit = $a11 .',' . $a21;
 	else {
 		$boucle->partie = ($a11 != 'n') ? $a11 : $a12;
 		$boucle->total_parties =  ($a21 != 'n') ? $a21 : $a22;
@@ -308,27 +279,25 @@ function calculer_critere_parties($idb, &$boucles, $param, $not, $match) {
 }
 
 function calculer_critere_parties_aux($idb, &$boucles, $param) {
-	ereg('^(([0-9]+)|n|(#.*))(-([0-9]+))?$', $param, $m);
-	if ($m[1] == 'n') 
-	  $a = 'n';
-	else {
-	  $a = simplifie_param_dynamique($m[1], $boucles, $idb);
+	if ($param[0]->type == 'texte')
+	  {
+	    ereg('^(([0-9]+)|n)(-([0-9]+))?$', $param[0]->texte, $m);
+	    return array($m[1], ($m[4] ? $m[4] : 0));
+	  } else {
+	  $a1 = calculer_liste(array($param[0]), array(), $idb, $boucles);
+	  ereg('^ *(-([0-9]+))?$', $param[1]->texte, $m);
+	  return array("intval($a1)", ($m[2] ? $m[2] : 0));
 	}
-	return array($a, ($m[5] ? $m[5] : 0));
 }
 
 //
-// La fonction d'aiguillage sur le nom du criteres
+// La fonction d'aiguillage sur le nom du critere
 //
 
 function calculer_criteres ($idb, &$boucles) {
 
-	foreach($boucles[$idb]->param as $param) {
-		// Analyse du critere
-		preg_match("/^([!]?)[[:space:]]*(debut|([a-z_]+))/ism",
-			$param, $match);
-		$critere = $match[2];
-		$not = ($match[1] == '!');
+	foreach($boucles[$idb]->criteres as $crit) {
+		$critere = $crit->op;
 
 		// critere personnalise ?
 		$f = "critere_".$critere;
@@ -336,235 +305,239 @@ function calculer_criteres ($idb, &$boucles) {
 			$f .= '_dist';
 
 		// fonction critere standard ?
-		if (!function_exists($f))
-			$f = 'calculer_critere_DEFAUT';
-
+		if (!function_exists($f)) {
+		  // double cas particulier repere a l'analyse lexicale
+		  if (($critere == ",") OR ($critere == '/'))
+		    $f = 'calculer_critere_parties';
+		  else	$f = 'calculer_critere_DEFAUT';
+		}
 		// Applique le critere
-		$res = $f($idb, $boucles, $param, $not);
+		$res = $f($idb, $boucles, $crit);
 
-		// gestion d'erreur
+		// Gestion d'erreur
 		if (is_array($res)) erreur_squelette($res);
 	}
 }
 
 # Criteres numeriques et de comparaison
 
-function calculer_critere_DEFAUT($idb, &$boucles, $param, $not) {
+function calculer_critere_DEFAUT($idb, &$boucles, $crit) {
+	
 	global $table_date, $tables_des_serveurs_sql;
 
 	$boucle = &$boucles[$idb];
 	$type = $boucle->type_requete;
-	$id_table = $boucle->id_table;
+	$col_table = $id_table = $boucle->id_table;
 	$primary = $boucle->primary;
 	$id_field = $id_table . '.' . $primary; 
+	$fct = '';
+
+	// cas d'une valeur comparee a elle-meme ou son referent
+	if (count($crit->param) ==0)
+	  { $op = '=';
+	    $col = $crit->op;
+	    $val = $crit->op;
+
+	    // Cas special {lang} : aller chercher $GLOBALS['spip_lang']
+	    if ($val == 'lang')
+	      $val = '".$GLOBALS[\'spip_lang\']."';
+	    else {
+	    // Si id_parent, comparer l'id_parent avec l'id_objet
+	    // de la boucle superieure.... faudrait verifier qu'il existe
+	      // pour eviter l'erreur SQL
+	      if ($val == 'id_parent')
+		$val = $primary;
+	      // Si id_enfant, comparer l'id_objet avec l'id_parent
+	      // de la boucle superieure
+	      else if ($val == 'id_enfant')
+		$val = 'id_parent';
+	      $val = array("addslashes(" .calculer_argument_precedent($idb, $val, $boucles) .")");
+	    }
+	  }
+	else
+	  {
+	    // comparaison explicite
+	    // le phraseur impose que le premier param soit du texte
+	    $params = $crit->param;
+	    $col = array_shift($params);
+	    $col = $col[0]->texte;
+	    $op = $crit->op;
+
+	    // fonction SQL ?
+	    if (ereg("([a-z_]+)\(([a-z_]+)\)", $col,$match3)) {
+	      $col = $match3[2];
+	      $fct = $match3[1];
+	    }
+
+	    $val = array();
+	    foreach ($params as $param)
+	      $val[] = calculer_liste($param, array(), $boucles, $idb);
+	    spip_log("vol0: $val[0]");
+	  }
 
-	if (ereg('^([0-9a-zA-Z#_}{-]+)([,/])([0-9a-zA-Z#_}{-]+)$', $param, $match))
-	  calculer_critere_parties($idb, $boucles, $param, $not, $match);
-
-		// Restriction de valeurs (implicite ou explicite)
-	else if (eregi('^(`?[a-z_]+\(?[a-z_]*\)?`?) *(\??)((!?)(<=?|>=?|==?|IN) *"?([^<>=!"]*))?"?$', $param, $match)) {
-	  		$op = $match[5] ? $match[5] : '=';
-
-			// Variable comparee
-			$col = $match[1];
-			// fonction SQL
-			$fct = '';
-			if (ereg("([a-z_]+)\(([a-z_]+)\)", $col,$match3)) {
-				$col = $match3[2];
-				$fct = $match3[1];
-			}
-			$col_table = $id_table;
-			// Valeur de comparaison
-			if ($match[3]) {
-				if (strtoupper($op) != 'IN') {
-					$val = calculer_param_dynamique($match[6], $boucles, $boucles[$idb]->id_parent);
-					// gestion d'erreur
-					if (is_array($val)) erreur_squelette($val);
-				}
-				else {
-				// traitement special des valeurs textuelles
-				  $val = calculer_params_dynamiques($match[6], $boucles, $idb);
-				}
-			}
-			
-			else {
-				$val = $match[1];
-
-				// Cas special {lang} : aller chercher $GLOBALS['spip_lang']
-				if ($val == 'lang')
-					$val = '".$GLOBALS[\'spip_lang\']."';
-				else {
-				// Si id_parent, comparer l'id_parent avec l'id_objet
-				// de la boucle superieure
-					if ($val == 'id_parent')
-						$val = $primary;
-				// Si id_enfant, comparer l'id_objet avec l'id_parent
-				// de la boucle superieure
-					else if ($val == 'id_enfant')
-						$val = 'id_parent';
-					$val = calculer_argument_precedent($idb, $val, $boucles) ;
-					if (ereg('^\$',$val))
-						$val = '" . addslashes(' . $val . ') . "';
-					else
-						$val = addslashes($val);
-				}
-
-			}
+	  // cas special: statut=
+	  // si on l'invoque dans une boucle il faut interdire
+	  // a la boucle de mettre ses propres criteres de statut
+	  // http://www.spip.net/@statut (a documenter)
+	if ($col == 'statut')
+		  $result->where['statut'] = '1';
 
-			if ($s = calculer_critere_externe($boucle, $id_field,$col, $type)) {
-				$col_table = $s;
-			}
+	if ($s = calculer_critere_externe($boucle, $id_field,$col, $type))
+		$col_table = $s;
 
-			// Cas particulier pour les raccourcis 'type_mot' et 'titre_mot'
-			else if ($type != 'mots'
+	// Cas particulier pour les raccourcis 'type_mot' et 'titre_mot'
+	else if ($type != 'mots'
 			AND ($col == 'type_mot' OR $col == 'titre_mot'
 			OR $col == 'id_groupe')) {
-				if ($type == 'forums')
-					$col_lien = "forum";
-				else if ($type == 'syndication')
-					$col_lien = "syndic";
-				else
-					$col_lien = $type;
-				$boucle->from[] = "spip_mots_$col_lien AS lien_mot";
-				$boucle->from[] = 'spip_mots AS mots';
-				$boucle->where[] = "$id_field=lien_mot." . $primary;
-				$boucle->where[] = 'lien_mot.id_mot=mots.id_mot';
-				$boucle->group = $id_field;
-				$boucle->select[] = $id_field; # pour postgres, neuneu ici
-				$col_table = 'mots';
-
-				$boucle->lien = true;
-				if ($col == 'type_mot')
-					$col = 'type';
-				else if ($col == 'titre_mot')
-					$col = 'titre';
-				else if ($col == 'id_groupe')
-					$col = 'id_groupe';
-			}
+		if ($type == 'forums')
+		  $col_lien = "forum";
+		else if ($type == 'syndication')
+		  $col_lien = "syndic";
+		else
+		  $col_lien = $type;
+		$boucle->from[] = "spip_mots_$col_lien AS lien_mot";
+		$boucle->from[] = 'spip_mots AS mots';
+		$boucle->where[] = "$id_field=lien_mot." . $primary;
+		$boucle->where[] = 'lien_mot.id_mot=mots.id_mot';
+		$boucle->group = $id_field;
+		$boucle->select[] = $id_field; # pour postgres, neuneu ici
+		$col_table = 'mots';
+
+		$boucle->lien = true;
+		if ($col == 'type_mot')
+		  $col = 'type';
+		else if ($col == 'titre_mot')
+		  $col = 'titre';
+		else if ($col == 'id_groupe')
+		  $col = 'id_groupe';
+	}
 
-			// Cas particulier : selection des documents selon l'extension
-			if ($type == 'documents' AND $col == 'extension')
-				$col_table = 'types_documents';
-			// HACK : selection des documents selon mode 'image'
-			// (a creer en dur dans la base)
-			else if ($type == 'documents' AND $col == 'mode'
-			AND $val == 'image')
-				$val = 'vignette';
-			// Cas particulier : lier les articles syndiques
-			// au site correspondant
-			else if ($type == 'syndic_articles' AND
-			!ereg("^(id_syndic_article|titre|url|date|descriptif|lesauteurs|id_document)$",$col))
-				$col_table = 'syndic';
-
-			// Cas particulier : id_enfant => utiliser la colonne id_objet
-			if ($col == 'id_enfant')
-				$col = $primary;
-			// Cas particulier : id_secteur = id_rubrique pour certaines tables
-			if (($type == 'breves' OR $type == 'forums') AND $col == 'id_secteur')
-				$col = 'id_rubrique';
-
-			// Cas particulier : expressions de date
-			if (ereg("^(date|mois|annee|heure|age|age_relatif|jour_relatif|mois_relatif|annee_relatif)(_redac)?$", $col, $regs)) {
-				$col = $regs[1];
-				if ($regs[2]) {
-					$date_orig = $id_table . ".date_redac";
-					$date_compare = '\'" . normaliser_date(' .
-					calculer_argument_precedent($idb, 'date_redac', $boucles) .
-					') . "\'';
-				}
-				else {
-					$date_orig = "$id_table." . $table_date[$type];
-					$date_compare = '\'" . normaliser_date(' .
-					  calculer_argument_precedent($idb, 'date', $boucles) .
-					  ') . "\'';
-				}
-
-				if ($col == 'date') {
-					$col = $date_orig;
-					$col_table = '';
-				}
-				else if ($col == 'mois') {
-					$col = "MONTH($date_orig)";
-					$col_table = '';
-				}
-				else if ($col == 'annee') {
-					$col = "YEAR($date_orig)";
-					$col_table = '';
-				}
-				else if ($col == 'heure') {
-					$col = "DATE_FORMAT($date_orig, '%H:%i')";
-					$col_table = '';
-				}
-				else if ($col == 'age') {
-					$col = calculer_param_date("now()", $date_orig);
-					$col_table = '';
-				}
-				else if ($col == 'age_relatif') {
-					$col = calculer_param_date($date_compare, $date_orig);
-					$col_table = '';
-				}
-				else if ($col == 'jour_relatif') {
-					$col = "LEAST(TO_DAYS(" .$date_compare . ")-TO_DAYS(" .
-					$date_orig . "), DAYOFMONTH(" . $date_compare .
-					")-DAYOFMONTH(" . $date_orig . ")+30.4368*(MONTH(" .
-					$date_compare . ")-MONTH(" . $date_orig .
-					"))+365.2422*(YEAR(" . $date_compare . ")-YEAR(" .
-					$date_orig . ")))";
-					$col_table = '';
-				}
-				else if ($col == 'mois_relatif') {
-					$col = "MONTH(" . $date_compare . ")-MONTH(" .
-					$date_orig . ")+12*(YEAR(" . $date_compare .
-					")-YEAR(" . $date_orig . "))";
-					$col_table = '';
-				}
-				else if ($col == 'annee_relatif') {
-					$col = "YEAR(" . $date_compare . ")-YEAR(" .
-					$date_orig . ")";
-					$col_table = '';
-				}
-			}
+	// Cas particulier : selection des documents selon l'extension
+	if ($type == 'documents' AND $col == 'extension')
+	  $col_table = 'types_documents';
+	// HACK : selection des documents selon mode 'image'
+	// (a creer en dur dans la base)
+	else if ($type == 'documents' AND $col == 'mode' AND $val[0] == "'image'")
+	  $val[0] = "'vignette'";
+	// Cas particulier : lier les articles syndiques
+	// au site correspondant
+	else if ($type == 'syndic_articles' AND
+		 !ereg("^(id_syndic_article|titre|url|date|descriptif|lesauteurs|id_document)$",$col))
+	  $col_table = 'syndic';
+
+	// Cas particulier : id_enfant => utiliser la colonne id_objet
+	if ($col == 'id_enfant')
+	  $col = $primary;
+	// Cas particulier : id_secteur = id_rubrique pour certaines tables
+	if (($type == 'breves' OR $type == 'forums') AND $col == 'id_secteur')
+	  $col = 'id_rubrique';
+
+	// Cas particulier : expressions de date
+	if (ereg("^(date|mois|annee|heure|age|age_relatif|jour_relatif|mois_relatif|annee_relatif)(_redac)?$", $col, $regs)) {
+	  $col = $regs[1];
+	  if ($regs[2]) {
+	    $date_orig = $id_table . ".date_redac";
+	    $date_compare = '\'" . normaliser_date(' .
+	      calculer_argument_precedent($idb, 'date_redac', $boucles) .
+	      ') . "\'';
+	  }
+	  else {
+	    $date_orig = "$id_table." . $table_date[$type];
+	    $date_compare = '\'" . normaliser_date(' .
+	      calculer_argument_precedent($idb, 'date', $boucles) .
+	      ') . "\'';
+	  }
 
-			if ($type == 'forums' AND
-			($col == 'id_parent' OR $col == 'id_forum'))
-				$boucle->plat = true;
+	  if ($col == 'date') {
+			$col = $date_orig;
+			$col_table = '';
+		}
+		else if ($col == 'mois') {
+			$col = "MONTH($date_orig)";
+			$col_table = '';
+		}
+		else if ($col == 'annee') {
+			$col = "YEAR($date_orig)";
+			$col_table = '';
+		}
+		else if ($col == 'heure') {
+			$col = "DATE_FORMAT($date_orig, '%H:%i')";
+			$col_table = '';
+		}
+		else if ($col == 'age') {
+			$col = calculer_param_date("now()", $date_orig);
+			$col_table = '';
+		}
+		else if ($col == 'age_relatif') {
+			$col = calculer_param_date($date_compare, $date_orig);
+			$col_table = '';
+		}
+		else if ($col == 'jour_relatif') {
+			$col = "LEAST(TO_DAYS(" .$date_compare . ")-TO_DAYS(" .
+			$date_orig . "), DAYOFMONTH(" . $date_compare .
+			")-DAYOFMONTH(" . $date_orig . ")+30.4368*(MONTH(" .
+			$date_compare . ")-MONTH(" . $date_orig .
+			"))+365.2422*(YEAR(" . $date_compare . ")-YEAR(" .
+			$date_orig . ")))";
+			$col_table = '';
+		}
+		else if ($col == 'mois_relatif') {
+			$col = "MONTH(" . $date_compare . ")-MONTH(" .
+			$date_orig . ")+12*(YEAR(" . $date_compare .
+			")-YEAR(" . $date_orig . "))";
+			$col_table = '';
+		}
+		else if ($col == 'annee_relatif') {
+			$col = "YEAR(" . $date_compare . ")-YEAR(" .
+			$date_orig . ")";
+			$col_table = '';
+		}
+	}
 
-			// Operateur de comparaison
-			if ($col_table) {
-				if ($col[0] == "`") 
-				  $col = "$col_table." . substr($col,1,-1);
-				else $col = "$col_table.$col";
-			}
+	if ($type == 'forums' AND
+	($col == 'id_parent' OR $col == 'id_forum'))
+		$boucle->plat = true;
 
-			if (strtoupper($op) == 'IN') {
-				// traitement special des valeurs textuelles
-				$where = "$col IN ($val)";
-				if ($match[4] == '!') {
-					$where = "NOT ($where)";
-				} else {
-					$boucle->default_order = 'rang';
-					$boucle->select[] =
-						"FIND_IN_SET($col, \\\"$val\\\") AS rang";
-				}
-			} else {
-				if ($op == '==') $op = 'REGEXP';
-				if ($fct) $col = "$fct($col)";
-				if ($match[4] == '!')
-					$where = "NOT ($col $op '$val')";
-				else
-					$where = "($col $op '$val')";
-
-				// operateur optionnel {lang?}
-				if ($match[2]) {
-					$champ = calculer_argument_precedent($idb, $match[1], $boucles) ;
-					$where = "\".($champ ? \"$where\" : 1).\"";
-				}
+	// Operateur de comparaison
+	if ($col_table) {
+		if ($col[0] == "`") 
+		  $col = "$col_table." . substr($col,1,-1);
+		else $col = "$col_table.$col";
+	}
+	spip_log("'$op'");
+	if (strtoupper($op) == 'IN') {
+	  // traitement special d'une suite de valeurs
+	  foreach ($val as $k => $v) {
+	    spip_log("IN '$v'");
+	    if (!ereg("^$", $v))
+	  // il manque le addslashes mais il faut d'abord eliminer la chaine
+	      // vide qui vient 
+	      $val[$k] = "\n'\" . addslashes(" . ($v) . ") . \"'";
+	  }
+	  $val = join(', ',$val);
+	  if (!ereg("^ *\(.*) *$", $val)) $val = "($val)";
+	  $where = "$col IN $val";
+	  if ($crit->not) {
+			$where = "NOT ($where)";
+		} else {
+			$boucle->default_order = 'rang';
+			$boucle->select[] =
+				"FIND_IN_SET($col, \\\"$val\\\") AS rang";
+		}
+	} else {
+		if ($op == '==') $op = 'REGEXP';
+		if ($fct) $col = "$fct($col)";
+		$where = "($col $op '\" . " . $val[0] . ' . "\')';
+		if ($crit->not)	$where = "NOT $where";
+
+		// operateur optionnel {lang?}
+		if ($crit->cond) {
+			$champ = calculer_argument_precedent($idb, $arg1, $boucles) ;
+			$where = "\".($champ ? \"$where\" : 1).\"";
+		}
 
-			}
-			$boucle->where[] = $where;
-	} // fin du if sur les restrictions de valeurs
-	else
-	erreur_squelette(_T('zbug_critere_inconnu', array('critere' => $param)));
+	}
+	$boucle->where[] = $where;
 }
 
 // traitement des relations externes par une jointure.
@@ -588,7 +561,6 @@ function calculer_critere_externe(&$boucle, $id_field, $col, $type)
 	return $col_table;
 }
 
-
 function calculer_param_date($date_compare, $date_orig) {
 	return
 	"LEAST((UNIX_TIMESTAMP(" .
@@ -614,72 +586,4 @@ function calculer_param_date($date_compare, $date_orig) {
 	")))";
 }
 
-//
-// Calculer les parametres
-//
-
-// $val = le parametre (exemple : #_boucle:TITRE
-// $boucle = le tableau des boucles
-// $idb = la boucle de reference :
-//   - dans le cas d'un inclure, la boucle courante
-//   - dans le cas d'un parametre de boucle, la boucle parente
-function calculer_param_dynamique($val, &$boucles, $idb) {
-#	if (ereg('^ *\((.*)) *$', $val, $m)) $val = $m[1]; # si on veut (#...)
-	if (ereg(NOM_DE_CHAMP . "([{][^}]*[}])?", $val, $regs)) {
-#spip_log(serialize($regs)." ($idb)");
-		$champ = new Champ;
-		$champ->nom_boucle = $regs[2];
-		$champ->nom_champ = $regs[3];
-		$champ->etoile = $regs[4];
-		$champ->id_boucle = $idb;
-		$champ->boucles = &$boucles;
-		phraser_args($regs[5], "", "", array(), $champ);
-		$champ = calculer_champ($champ);
-		return '" . addslashes(' . $champ . ') . "';
-
-	} else {
-	  if ($val[0]== '%') {
-	    spip_log($val .
-		     " est obsolete; utiliser ENV{" .  substr($val,1) . "}");
-		  return '" . addslashes($Pile[0][\''. substr($val,1)  ."']) . \"";
-	  }
-		else
-		  return addslashes($val);
-	}
-}
-
-/*
-// une version acceptant les champs etendus  serait en gros:
-function calculer_param_dynamique($val, &$boucles, $idb) {
-  
-return '" . addslashes(' .
-	calculer_liste(phraser_champs_etendus($val, array()),
-			 array(), $boucles, $idb)
- . ') . "';
-}
-/* */
-
-//
-function calculer_params_dynamiques($liste, &$boucles, $idb) {
-	ereg("^ *\(?(.*[^)])\)? *$",$liste, $reg);
-	$res = array();
-	foreach (split(" *, *", $reg[1]) as $v) {
-	  $v = calculer_param_dynamique($v, $boucles, $boucles[$idb]->id_parent);
-	  if (is_array($v)) erreur_squelette($v);
-	  if (strpos('0123456789',$v[0]) !== false)
-	    $res[] = $v;
-	  else if ($v[0]=='"')
-	    $res[] = "'" . $v . "'";
-	  else
-	    $res[] = "'$v'";
-	}
-	return join(',', $res);
-}
-
-function simplifie_param_dynamique($val, &$boucles, $idb)
-{
-	$a = calculer_param_dynamique($val, $boucles, $boucles[$idb]->id_parent);
-	if (!ereg('" \. *(.*)\. "', $a, $m)) return $a;
-	return $m[1];
-}	
 ?>
diff --git a/inc-html-squel.php3 b/inc-html-squel.php3
index d4b742c489a1674aa3d2c83241be210ba66a016f..019d990efe22278f7ac4ae5050438cf8b97e17d9 100644
--- a/inc-html-squel.php3
+++ b/inc-html-squel.php3
@@ -23,16 +23,14 @@ define("_INC_HTML_SQUEL", "1");
 define('NOM_DE_BOUCLE', "[0-9]+|[-_][-_.a-zA-Z0-9]*");
 define('NOM_DE_CHAMP', "#((" . NOM_DE_BOUCLE . "):)?([A-Z_]+)(\*?)");
 define('CHAMP_ETENDU', '\[([^]\[]*)\(' . NOM_DE_CHAMP . '([^[)]*\)[^]\[]*)\]');
-define('PARAM_DE_BOUCLE','[[:space:]]*[{][[:space:]]*([^}{]*([{][^}]*[}][^}]*)*)[[:space:]]*[}]');
+define('PARAM_DE_BOUCLE','[[:space:]]*[{][[:space:]]*([^{}]*([{][^[}]]*[}][^[}]]*)*)[[:space:]]*[}]');
 define('TYPE_DE_BOUCLE', "[^)]*");
 define('BALISE_DE_BOUCLE',
 	"^<BOUCLE(" .
 	NOM_DE_BOUCLE .
 	')[[:space:]]*\((' .
 	TYPE_DE_BOUCLE .
-	')\)((' .
-	PARAM_DE_BOUCLE .
-	')*)[[:space:]]*>');
+	')\)');
 define('PARAM_INCLURE','^[[:space:]]*[{][[:space:]]*([_0-9a-zA-Z]+)[[:space:]]*(=)?');
 define('BALISE_INCLURE','<INCLU[DR]E[[:space:]]*\(([^)]*)\)');
 define('DEBUT_DE_BOUCLE','/<B('.NOM_DE_BOUCLE.')>.*?<BOUCLE\1[^-_.a-zA-Z0-9]|<BOUCLE('.NOM_DE_BOUCLE.')/ms');	# preg
@@ -47,7 +45,7 @@ function phraser_inclure($texte, $result) {
 		$texte = substr($texte, $p+strlen($match[0]));
 		// on assimile {var=val} a une liste de un argument sans fonction
 		phraser_args($texte,">","",$result,$champ);
-		foreach ($champ->args as $k => $v) {
+		foreach ($champ->param as $k => $v) {
 		  $var = $v[1][0];
 		  if ($var->type != 'texte')
 			erreur_squelette(_T('zbug_parametres_inclus_incorrects'),
@@ -55,16 +53,16 @@ function phraser_inclure($texte, $result) {
 		  else {
 		    ereg("^([^=]*)(=)?(.*)$", $var->texte,$m);
 		    if ($m[2]) {
-		      $champ->args[$k][0] = $m[1];
+		      $champ->param[$k][0] = $m[1];
 		      $val = $m[3];
 		      if (ereg('^[\'"](.*)[\'"]$', $val, $m)) $val = $m[1];
-		      $champ->args[$k][1][0]->texte = $val;
+		      $champ->param[$k][1][0]->texte = $val;
 		    }
 		    else
-		      $champ->args[$k] = array($m[1]);
+		      $champ->param[$k] = array($m[1]);
 		  }
 		}
-		$texte = $champ->apres;
+		$texte = substr($champ->apres,1);
 		$champ->apres = "";
 		$result[] = $champ;
 	}
@@ -84,7 +82,7 @@ function phraser_polyglotte($texte,$result) {
 		$lang = '';
 		$bloc = $match[1];
 		$texte = substr($texte,$p+strlen($match[0]));
-		while (preg_match("/^[[:space:]]*([^[{]*)[[:space:]]*[{\[]([a-z_]+)[}\]](.*)$/si", $bloc, $regs)) {
+		while (preg_match("/^[[:space:]]*([^[{]*)[[:space:]]*[[{]([a-z_]+)[]}](.*)$/si", $bloc, $regs)) {
 		  $trad = $regs[1];
 		  if ($trad OR $lang) 
 			$champ->traductions[$lang] = $trad;
@@ -159,17 +157,24 @@ function phraser_champs_etendus($texte, $result) {
 //  Analyse les filtres d'un champ etendu et affecte le resultat
 // renvoie la liste des lexemes d'origine augmentee
 // de ceux trouves dans les arguments des filtres (rare)
+// sert aussi aux arguments des includes et aux criteres de boucles
+// Tres chevelu
 
 function phraser_args($texte, $fin, $sep, $result, &$pointeur_champ) {
   $texte = ltrim($texte);
-  while (($texte!=="") && $texte[0] != $fin) {
+  while (($texte!=="") && strpos($fin, $texte[0]) === false) {
       ereg("^(\|?[^{)|]*)(.*)$", $texte, $match);
       $suite = ltrim($match[2]);
       $fonc = $match[1];
       if ($fonc[0] == "|") $fonc = substr($fonc,1);
       $res = array(trim($fonc));
       $args = $suite;
-      if ($suite[0] == '{') {
+      if ($suite[0] != '{')
+	{ if (!$match[1]) {
+	    erreur_squelette(_T('zbug_info_erreur_squelette'), $texte);
+	    break;
+	  }
+	} else {
 	$args = ltrim(substr($suite,1)); 
 	$collecte = array();
 	while ($args && $args[0] != '}') {
@@ -177,48 +182,74 @@ function phraser_args($texte, $fin, $sep, $result, &$pointeur_champ) {
 			ereg ('^(")([^"]*)(")(.*)$', $args, $regs);
 		else if ($args[0] == "'")
 			ereg ("^(')([^']*)(')(.*)$", $args, $regs);
-		else
-			ereg("^( *)([^,}{]*([{][^}{]*[}][^,}{]*)*[^,}]*)([,}$fin].*)$", $args, $regs);
+		else {
+		  ereg("^( *)([^,}]*)([,}$fin].*)$", $args, $regs);
+		  if (!strlen($regs[2]))
+		    {
+		      erreur_squelette(_T('zbug_info_erreur_squelette'), $args);
+		      $args = '';
+		      exit;
+		      }   
+		}
 
-		$args = ltrim($regs[count($regs)-1]);
 		$arg = $regs[2];
-		if (trim($regs[1])) { // valeur = ", ', ou vide
+		if (trim($regs[1])) {
 			$champ = new Texte;
 			$champ->texte = $arg;
+			$champ->apres = $champ->avant = $regs[1];
 			$result[] = $champ;
 			$collecte[] = $champ;
-		} else {   // valeur = ", ', ou vide
-		  if ((count($regs) > 3) &&
-		      ereg("^(.*)(#[A-Z0-9:_=]+[{].*[}])(.*)$",$arg, $r))
-		    {
-		      // champ avec arg, sans les [( )]: on les rajoute
-		      $arg = $r[1] .  '[(' . $r[2] . ')' . $r[3] . ']';
-		      $arg = phraser_champs_etendus($arg, array());
-		      
-		    } else {
-		    $arg = phraser_champs_exterieurs(ltrim($arg), $sep, $result);
+			$args = ltrim($regs[count($regs)-1]);
+		} else {
+		  if (!ereg("^(.*)" . NOM_DE_CHAMP ."[|{]", $arg, $r)) {
+		    $arg = phraser_champs_exterieurs($arg, $sep, $result);
+		    $args = ltrim($regs[count($regs)-1]);
+		    $collecte = array_merge($collecte, $arg);
+		    $result = array_merge($result, $arg);
+		  }
+		  else {
+		    $pred = $r[1];
+		    $par = ',}';
+		    if (ereg('(.*)\($', $pred, $m))
+		      {$pred = $m[1]; $par =')';}
+		    if ($pred) {
+			$champ = new Texte;
+			$champ->texte = $pred;
+			$champ->apres = $champ->avant = "";
+			$result[] = $champ;
+			$collecte[] = $champ;
+		    }
+		    $rec = substr($args, strpos($r[0],$args)+strlen($r[0])-1);
+		    $champ = new Champ;
+		    $champ->nom_boucle = $r[3];
+		    $champ->nom_champ = $r[4];
+		    $champ->etoile = $r[5];
+		    phraser_args($rec, $par, $sep, array(), $champ);
+		    $args = $champ->apres ;
+		    $champ->apres = '';
+		    if ($par==')') $args = substr($args,1);
+		    $collecte[] = $champ;
+		    $result[] = $champ;
 		  }
-		  $collecte = array_merge($collecte, $arg);
-		  $result = array_merge($result, $arg);
 		}
-		$args = ltrim($args);
 		if ($args[0] == ',') {
-		  $args = substr($args,1);
+		  $args = ltrim(substr($args,1));
 		  if ($collecte)
 		    {$res[] = $collecte; $collecte = array();}
 		}
+
 	}
 	if ($collecte) {$res[] = $collecte; $collecte = array();}
 	$args = substr($args,1);
       }
       $n = strlen($suite) - strlen($args);
-      $pointeur_champ->args[] = $res;
+      $pointeur_champ->param[] = $res;
       // pour les balises avec faux filtres qui boudent ce dur larbeur
       $pointeur_champ->fonctions[] = array($fonc, substr($suite, 0, $n));
       $texte = ltrim($args);
   }
-  # virer la parenthese fermante ou le chevron fermant
-  $pointeur_champ->apres = substr($texte,1);
+  # laisser l'appelant virer le caractere fermant
+  $pointeur_champ->apres = $texte;
   return $result;
 }
 
@@ -244,7 +275,7 @@ function phraser_champs_interieurs($texte, $sep, $result) {
 		// phraser_args indiquera ou commence apres
 		$result = phraser_args($regs[6], ")", $sep, $result, $champ);
 		$champ->avant = phraser_champs_exterieurs($regs[1],$sep,$result);
-		$champ->apres = phraser_champs_exterieurs($champ->apres,$sep,$result);
+		$champ->apres = phraser_champs_exterieurs(substr($champ->apres,1),$sep,$result);
 
 
 		$p = strpos($texte, $regs[0]);
@@ -264,55 +295,123 @@ function phraser_champs_interieurs($texte, $sep, $result) {
 	  else return phraser_champs_exterieurs($x, $sep, $result);}
 }
 
+// analyse des criteres de boucle, 
+
+function phraser_criteres($params, &$result) {
 
-function phraser_param($params, &$result) {
-	$params2 = array();
 	$args = array();
 	$type = $result->type_requete;
-	while (ereg('^' . PARAM_DE_BOUCLE . '[[:space:]]*(.*)$', trim($params), $m)) {
-		$params = $m[3];
-		$param = trim($m[1]);
+	foreach($params as $v) {
+		$var = $v[1][0];
+		$param = ($var->type != 'texte') ? "" : $var->texte;
+		if ((count($v) > 2) && (!eregi("[^A-Za-z]IN[^A-Za-z]",$param)))
+		  {
+// plus d'un argument:
+// c'est soit le critere LIMIT debut,fin si ça se termine par un chiffre
+// soit le critere PAR soit un critere perso
+		       
+			if (($var->type != 'texte') ||
+			    (strpos("0123456789", $param[strlen($param)-1])
+			     !== false))
+			  $op = ',';
+			else {
+			  ereg("^([a-zA-Z][a-zA-Z0-9]*) *(.*)$", $param, $m);
+			  $op = $m[1];
+			  $v[1][0]->texte = $m[2];
+			}
+			array_shift($v);
+			$crit = new Critere;
+			$crit->op = $op;
+			$crit->not = "";
+			$crit->param = $v;
+			$args[] = $crit;
+		  } else {
+
+		  if ($var->type != 'texte')
+			  erreur_squelette('criteres','');
+		  else {
 	// traiter qq lexemes particuliers pour faciliter la suite
-		if (($param == 'tout') OR ($param == 'tous'))
-			$result->tout = true;
-		else if ($param == 'plat') 
-			$result->plat = true;
-	// les separateurs (specs CSS3 aN+b a finaliser)
-		else if (ereg('^"([^"}]*)"( *, *(\-?[0-9]*)n)?(\+?([0-9]+))?)?$', $param, $m))
-			$result->separateur[] = $m[1];
+
+	// les separateurs
+			if ($var->apres)
+				$result->separateur[] = $param;
+			elseif (($param == 'tout') OR ($param == 'tous'))
+				$result->tout = true;
+			elseif ($param == 'plat') 
+				$result->plat = true;
 
 	// Boucle hierarchie, analyser le critere id_article - id_rubrique
 	// - id_syndic, afin, dans les cas autres que {id_rubrique}, de
 	// forcer {tout} pour avoir la rubrique mere...
 
-		else if (($type == 'hierarchie') &&
-			($param == 'id_article' OR $param == 'id_syndic'))
-			$result->tout = true;
-		else if (($type == 'hierarchie') && ($param == 'id_rubrique'))
-			{;}
-		else { 
-		  $params2[] = ($param == 'unique') ? 'doublons' :$param;
-		  /* pour bientot
-		   if (ereg('^([0-9a-zA-Z#_}{-]+)([,/])([0-9a-zA-Z#_}{-]+)$', $param, $match))
-			  $args['parties'] = $match;
-			else if (eregi('^(`?[a-z_]+\(?[a-z_]*\)?`?) *(\??)(!?)(<=?|>=?|==?|IN) *"?([^<>=!"]*)"?$', $param, $match))
-			  $args['comparaison'] = $match;
-			else {
-			  preg_match("/^([!]?)[[:space:]]*(debut|([a-z_]+))/ism", $param, $match);
-			  // contient aussi les comparaisons implicites !
-			  $args[$match[2]] = $match;
-		  */
+			elseif (($type == 'hierarchie') &&
+				($param == 'id_article' OR $param == 'id_syndic'))
+				$result->tout = true;
+			elseif (($type == 'hierarchie') && ($param == 'id_rubrique'))
+				{;}
+			else { 
+			  // pas d'emplacement statique, faut un dynamique
+			  /// mais il y a 2 cas qui ont les 2 !
+			  if (($param == 'unique') || ($param == 'doublons'))
+			    {
+			      // sera remplace ensuite par la bonne valeur
+			      // mais il faut l'indiquer tout de suite
+			      $result->doublons = true;
+			      $param = 'doublons';
+			    }
+			  elseif ($param == 'recherche')
+			    // meme chose (a cause de #nom_de_boucle:URL_*)
+			      $result->hash = true;
+
+			  if (ereg('^([0-9-]+)(/)([0-9-]+)$', $param, $m)) {
+			    $v[0] = $v[1];
+			    $v[0][0]->texte = $m[1];
+			    $v[1][0]->texte = $m[2];
+			    $crit = new Critere;
+			    $crit->op = '/';
+			    $crit->not = "";
+			    $crit->param = $v;
+			  } elseif (ereg('^(`?[A-Za-z_]+\(?[A-Za-z_]*\)?`?) *(\??)(!?)(<=?|>=?|==?| IN) *"?([^<>=!"]*)"?$', $param, $m)) {
+			    $v[0] = $v[1];
+			    $v[0][0]->texte = $m[1];
+			    $v[1][0]->texte = $m[5];
+			    $crit = new Critere;
+			    $crit->param = $v;
+			    $crit->op = trim($m[4]);
+			    $crit->not = $m[3];
+			    $crit->cond = $m[2];
+			    if ($m[1] == 'lang_select') $crit->op = $m[1];
+		  } elseif (preg_match("/^([!]?)[[:space:]]*([a-z_]+)[[:space:]]*(\??)(.*)$/ism", $param, $m)) {
+
+		  // contient aussi les comparaisons implicites !
+			    array_shift($v);
+			    if ($m[4])
+			      $v[0][0]->texte = $m[4];
+			    else {
+			      array_shift($v[0]);
+			      if (!$v[0]) array_shift($v);
+			    }
+			    $crit = new Critere;
+			    $crit->op = $m[2];
+			    $crit->param = $v;
+			    $crit->not = $m[1];
+			    $crit->cond = $m[3];
+			  }
+			  else
+			    erreur_squelette(_T('zbug_critere_inconnu',
+						array('critere' => $param)));
+			  $args[] = $crit;
+			}
+		  }
 		}
 	}
 
-	$result->param = $params2;
-	// pour bientot	$result->args = $args2;
+	$result->criteres = $args;
 }
 
 function phraser($texte, $id_parent, &$boucles, $nom) {
 
 	$all_res = array();
-
 	while (preg_match(DEBUT_DE_BOUCLE, $texte, $regs)) {
 		$nom_boucle = $regs[1].$regs[2];
 		$p = strpos($texte, '<BOUCLE'.$nom_boucle);
@@ -330,12 +429,11 @@ function phraser($texte, $id_parent, &$boucles, $nom) {
 		//
 		$debut = substr($texte, 0, $p);
 		$milieu = substr($texte, $p);
-
 		if (!ereg(BALISE_DE_BOUCLE, $milieu, $match)) {
 			erreur_squelette((_T('zbug_erreur_boucle_syntaxe')), $milieu);
 		}
+		$milieu = substr($milieu, strlen($match[0]));
 		$id_boucle = $match[1];
-
 		$result = new Boucle;
 		$result->id_parent = $id_parent;
 		$result->id_boucle = $id_boucle;
@@ -354,12 +452,15 @@ function phraser($texte, $id_parent, &$boucles, $nom) {
 		//
 		if (substr($type, 0, 6) == 'boucle') {
 			$result->type_requete = 'boucle';
-			$result->param = substr($match[2], 6);
+			$result->param[0] = substr($match[2], 6);
+			$milieu = substr($milieu, strpos($milieu, '>'));
 		} else {
 			$result->type_requete = $type;
-			phraser_param($match[3], $result);
+			phraser_args($milieu,">","",$all_res,$result);
+			phraser_criteres($result->param, $result);
+			$milieu = substr($result->apres,1);
+			$result->apres = "";
 		}
-
 		//
 		// Recuperer la partie conditionnelle avant
 		//
@@ -369,7 +470,7 @@ function phraser($texte, $id_parent, &$boucles, $nom) {
 			$result->avant = substr($debut, $p + strlen($s));
 			$debut = substr($debut, 0, $p);
 		}
-		$milieu = substr($milieu, strlen($match[0]));
+
 		if (strpos($milieu, $s)) {
 			erreur_squelette(_T('zbug_erreur_boucle_syntaxe'),
 				$id_boucle . 
@@ -388,7 +489,6 @@ function phraser($texte, $id_parent, &$boucles, $nom) {
 		}
 		$texte = substr($milieu, $p + strlen($s));
 		$milieu = substr($milieu, 0, $p);
-
 		//
 		// 1. Recuperer la partie conditionnelle apres
 		//
@@ -408,7 +508,6 @@ function phraser($texte, $id_parent, &$boucles, $nom) {
 			$result->altern = substr($texte, 0, $p);
 			$texte = substr($texte, $p + strlen($s));
 		}
-
 		$result->avant = phraser($result->avant, $id_parent,$boucles, $nom);
 		$result->apres = phraser($result->apres, $id_parent,$boucles, $nom);
 		$result->altern = phraser($result->altern,$id_parent,$boucles, $nom);
@@ -423,8 +522,6 @@ function phraser($texte, $id_parent, &$boucles, $nom) {
 		} else
 			$boucles[$id_boucle] = $result;
 	}
-
 	return phraser_champs_etendus($texte, $all_res);
 }
-
 ?>