affiche la structure des blocs de l'article.
// ce fichier doit être généré par placid, à partir du fichier
// spipBlocs.grammar
require("spipBlocs_parser.php");
// on représente l'arbre syntaxique d'un texte spip par une arbo
// d'éléments, mais on représente cet arbre dans une liste à plat.
// Ainsi, si on a l'arbre suivant :
// A-B
// +C-D
// | +E
// | +F-G
// +H
//
// On le représente par A/0, B/1, C/1, D/2, E/2, F/2, G/3, H/1
class Element {
var $type; // type de raccourci associé
var $niveau; // profondeur de cet élément dans l'arbre
var $content; // contenu textuel
function Element($type) {
global $niveau;
//error_log("new Element($type)");
$this->type= $type;
$this->niveau= $niveau;
$this->content= null;
}
}
function traiterInline($contenu) {
return $contenu; // ' '.preg_replace('/\s+/', ' ', $contenu);
}
/** génère le tag fermant correspondant à un type de bloc liste ou parag */
function fermer($type) {
switch($type) {
case 'ul': return "\n/UL";
case 'ol': return "\n/OL";
case 'cadre': return "\n/CADRE";
case 'quote': return "\n/QUOTE";
case 'html': return "\n/HTML";
case 'poesie': return "\n/POESIE";
case 'intertitre': return "\n/INTERTITRE";
case '' : return "\n/PARAG";
default: die("Type d'état inconnu : '$type'");
}
}
/** génère le tag ouvrant correspondant à un type de bloc liste ou parag */
function ouvrir($type) {
switch($type) {
case 'ul': return "\nUL";
case 'ol': return "\nOL";
case 'cadre': return "\nCADRE";
case 'quote': return "\nQUOTE";
case 'html': return "\nHTML";
case 'poesie': return "\nPOESIE";
case 'intertitre': return "\nINTERTITRE";
case '' : return "\nPARAG";
default: die("Type d'état inconnu : '$type'");
}
}
/**
* permet de fermer les tags d'avant et d'en ouvrir de nouveaux
* pour passer d'un état courant à celui imposé par l'élément passé en
* paramètre. En clair, si on change de niveau ou de type deliste à
* puce, on ferme/ouvre ce qu'il faut, et si on change de paragraphe,
* on fait "ce qu'il faut"
*/
function adapterEtats(&$etats, $elem=null) {
$res='';
error_log("adapterEtats".$elem->type.":".$elem->content["niveau"]
."/".$elem->content["type"]." -> ".print_r($etats, true));
if (empty($elem)) {
$niveauDst= 0;
$typeDst= '';
} elseif($elem->type=="Puce") {
$niveauDst= &$elem->content["niveau"];
$typeDst = &$elem->content["type"];
} else { // Parag
error_log("adapterEtats Parag".$elem->content);
$niveauDst= 1;
$typeDst= '';
}
// ouvrir ceux qui manquent (si la pile est vide, ou si on
// passe de -* à -### par ex.)
if(count($etats)<$niveauDst) {
while(count($etats)<$niveauDst) {
$res.= ouvrir($typeDst);
array_unshift($etats, $typeDst);
}
// auquel cas, on a forcement fini
return $res;
}
// ou fermer les niveaux en trop
while(count($etats)>$niveauDst) {
$res.= fermer($etats[0]);
array_shift($etats);
}
// ensuite, si ça change de type, ou que c'est un paragraphe,
// on ferme l'ancien et on en ouvre un nouveau
if($niveauDst!=0 && ($typeDst!=$etats[0] || $elem->type=='Saut')) {
$res.= fermer($etats[0]);
$etats[0]= &$typeDst;
$res.= ouvrir($etats[0]);
}
return $res;
}
$debutBlocs=array(
'Tableau' => "\nTABLEAU",
'Ligne' => "\nTR",
'Cellule' => "\nTD",
'Quote' => "\nQUOTE",
'Code' => "\nCODE",
'Cadre' => "\nCADRE",
'Html' => "\nHTML",
'Poesie' => "\nPOESIE",
'Intertitre' => "\nINTERTITRE",
'Puce' => "\nLI"
);
$finBlocs=array(
'Tableau' => "\n/TABLEAU",
'Ligne' => "\n/TR",
'Cellule' => "\n/TD",
'Quote' => "\n/QUOTE",
'Code' => "\n/CODE",
'Cadre' => "\n/CADRE",
'Html' => "\n/HTML",
'Poesie' => "\n/POESIE",
'Intertitre' => "\n/INTERTITRE",
'Puce' => "\n/LI"
);
/**
* Là .. l'algo se complique sur certains points, du fait que les
* raccourcis ne définissent pas clairement les limites de certains
* blocs (paragraphe et puce surtout).
* Le principe consiste à parcourir le tableau en effectuant des appels
* récursifs quand on change de niveau dans le tableau. Ainsi, il
* "suffit" de dumper chaque niveau et de l'insérer dans le bloc au dessus.
* Là où c'est galère, c'est pour les les paragraphes, et les
* imbrications de listes à puces dont il faut tenir le compte. Pour
* ça, on a une "pile" des états (parag, ol ou ul) pour chaque niveau
* d'appel récursif.
*/
function dump($niveau=0) {
global $arbreBlocs, $indexBlocs, $debutBlocs, $finBlocs;
$etats= array();
// error_log("DUMP $niveau");
while($indexBlocs < count($arbreBlocs)) {
$elem= &$arbreBlocs[$indexBlocs];
// error_log("DUMP elem ".$elem->type.":".$elem->niveau." ($niveau)");
if($elem->niveau > $niveau) {
$sousBloc= dump($niveau+1);
switch($last->type) {
case 'Tableau' :
case 'Ligne' :
case 'Cellule' :
case 'InterTitre' :
case 'Quote' :
case 'Cadre' :
case 'Puce' :
$contenu.= adapterEtats($etats, $last);
$contenu.= $debutBlocs[$last->type]
.$sousBloc.$finBlocs[$last->type];
break;
case 'Html' :
$contenu.= adapterEtats($etats);
$contenu.= $debutBlocs[$last->type].$sousBloc
.$finBlocs[$last->type];
break;
case 'Code' :
$contenu.= adapterEtats($etats);
$contenu.= $debutBlocs[$last->type].$sousBloc
.$finBlocs[$last->type];
break;
case 'Poesie' :
$contenu.= adapterEtats($etats);
$contenu.= $debutBlocs[$last->type]
.str_replace("\n", "
\n", $sousBloc)
.$finBlocs[$last->type];
break;
default: $contenu.= "\n$last->type : ".$sousBloc."\n";
break;
}
// error_log("apres dump ".($niveau+1));
} elseif($elem->niveau < $niveau) {
// error_log("FIN $niveau -> ".$elem->niveau);
break;
} else {
switch($elem->type) {
case '' : $contenu.= traiterInline($elem->content);
break;
case 'Saut' :
switch($elem->content) {
case '':
$contenu.= adapterEtats($etats, $elem);
break;
case '_': $contenu.= "
\n";
break;
case '-': $contenu.= "\n";
break;
}
break;
default: $last= &$elem;
break;
}
$indexBlocs++;
}
}
// finir la pile de trucs en stock
$contenu.= adapterEtats($etats, null);
return $contenu;
}
// ajoute un élément dans l'arbre, au niveau courant (élément
// unitaire par exemple)
function &addElement($type) {
global $pile, $arbreBlocs, $niveau, $typeCourant;
//error_log("$niveau : addElement($type)");
// créer un nouveau noeud et l'ajouter à l'arbre
$courant= &new Element($type);
array_push($arbreBlocs, &$courant);
return $courant;
}
// ajoute un élément dans l'arbre, dans une nouvelle branche
function &pushElement($type) {
global $pile, $arbreBlocs, $niveau, $typeCourant;
// empiler le noeud précédant
array_push($pile, $typeCourant);
$courant= &addElement($type);
$typeCourant= $type;
$niveau++;
return $courant;
}
function &pushText(&$text) {
//error_log("pushText($text)");
global $arbreBlocs;
$courant=&new Element(null, true);
$courant->content= $text;
array_push($arbreBlocs, &$courant);
return $courant;
}
function popElement($type) {
//error_log("popElement($type)");
global $pile, $arbreBlocs, $typeCourant, $niveau;
if($type!=$typeCourant) {
var_dump($arbreBlocsp);
die("Mismatch entre '".$courant->type."' et '$type'");
}
$niveau--;
$typeCourant= array_pop($pile);
}
if ($_SERVER["argv"][1]=="-fichier") {
$fichier=$_SERVER["argv"][2];
} else {
$fichier= "zz";
}
$f = fopen($fichier, "r") or die("peut pas ouvrir $fichier");
$buffer = fread($f, filesize($fichier));
fclose($f);
tic();
$pile=array();
$arbreBlocs=array();
$niveau=0;
$courant= &pushElement("PRINCIPAL", false);
$typeCourant= "PRINCIPAL";
$parser=new spipBlocs_parser();
$parser->parse($buffer);
tac("parsing 1");
var_dump($arbreBlocs);
echo "count : ".count($arbreBlocs);
$indexBlocs= 0;
$contenu= dump();
echo $contenu;
?>