[spip-dev] Placid, le parser de Pif

Salut les spipeurs,
  J'étais en congés la semaine dernière, et je viens juste de me
réabonner, mais j'ai vu ce mail d'Antoine dans les archives.

  J'ai un peu profité de ces congés pour avancer sur mon parser (et
beaucoup pour avancer sur ma véranda, mais c'est un autre débat).
  J'ai maintenant une version qui marche, qui génère un squelette
apparemment identique à celui avec le parseur d'origine (à la
numérotation des boucles près => un diff est pas vide), et qui ne va
"que" 2 fois moins vite que l'original ...

  Je pose ça dans un coin d'ici peu.

Remarques en vrac :

  Heu, avant ça, juste une question : pourquoi placid ? c'est une
bonne idée, mais t'as trouvé un acronyme de la mort à coller dessus ?
Parseur Lourd Affreux Complètement Impossible à Débugger ?

- heu, peux-tu régler ton éditeur pour indenter avec des tabs ? :wink:

  En fait, j'aime pô les indentations à 8 caractères, ça oblige à
élargir la fenêtre. Je préfère à 4, du coup, c'est un peu pénible :wink:
mais j'vais refaire une passe pour améliorer ça en disant à emacs de
faire des tab de 4 caractères (même si c'est contre ma religion :wink:

- ton parser a un problème majeur : il accroche sur des
caractères spécifiques plutôt que de chercher les structures
directement (oui, je sais, c'est comme ça qu'on fait (tm),
mais bon)

  Si on veut généraliser un tout petit peu le système, on arrive
vite à une solution comme ça en fait.

- du coup, il est lent puisque le chevron "<" est très utilisé
en HTML...

  Effectivement, ça vous dirait pas de changer de délimiteur pour
les squelette ? :wink: mais d'un autre coté, il s'agit là de millisecondes.
  Sur un squelette comme article-dist v1.7a7 on passe de 125ms à 250ms
sur mon pc pour générer le tableau de boucles/champs/textes, c'est
quand même pas la mort, d'autant plus que ça n'est censé être fait que
quand il y a des modifs du squelette, c'est à dire presque jamais sur
un site en prod.
  Pour info, les 250ms, c'est à peu près
- 100ms pour repérer les tokens
- 100ms pour créer la structure de boucles
- 50ms pour repasser dessus et créer les requêtes mysql associées.
  (j'ai découvert microtime la semaine dernière :wink:

- on le rend beaucoup moins lent en utilisant preg_match (note
que le parser de spip lui, s'en passe plutôt bien, et que
preg_match n'est pas toujours dispo sous php3...)

  Pour l'instant, j'ai essayé de rester "php3 compliant", ne serait-ce
que pour pouvoir comparer avec l'existant.

- d'autre part, toujours à cause du problème susnommé, tu
dois gérer une espèce de backtrack pour retrouver les textes
conditionnels avant

  Pas vraiment, au contraire. Actuellement, on cherche un <BOUCLE et
ensuite on cherche un éventuel <B avant. Dans ma version, on cherche
<B ou <BOUCLE et c'est le premier des 2 qu'on trouve qui effectue la
création de l'objet boucle.
  Par contre, là où c'est galère, c'est pour savoir quand on est
arrivé à la fin d'une boucle, puisqu'il n'y à pas forcément de /B ou
de //B.

- bugs potentiels : que se passe-t-il si j'écris
(#EMAIL) ? ce n'est pas un champ étendu, c'est juste un
champ que je veux afficher entre parenthèses :stuck_out_tongue_winking_eye:

  À vérifier, mais je crois que ça marche, même s'il y a un [ 3
kilomètres avant.

En fait je pensais adopter (dans l'éventualité où je bougerais
ma flemme et écrirais moi-même un tel machin) une méthode
plus adaptée à SPIP, c'est-à-dire :

- pas de langage de grammaire générique (enfin on peut,
mais pour SPIP ce n'est pas utile vu le peu de structures
différentes)

  En fait, l'une des grosses modifs de cette semaine, c'est que j'ai
essayé d'isoler complètement l'outil de génération pour en faire un
produit GPL indépendant.
  J'ai également changé le système d'analyse lexicale : je commence
par parser tout le buffer pour obtenir un tableau de tokens qu'il n'y
a plus qu'à parcourir pendant l'analyse syntaxique. Ça accélère pas
mal les choses.

- plutôt qu'un tableau de regexp, écrire une classe de
parsing dédiée pour chaque structure (class ParserChamp,
class ParserBoucle, etc.)
[...]

  L'idée est séduisante (et pis c'est objet, ça va plaire à Marc ;-).
  Mais ce qui est difficile à faire c'est de récupérer le cas ou
l'un des matchs retourne une position qui est au milieu du bloc isolé
par un autre match. Il faut savoir quand rappeler quel match, c'est
assez casse g.. à coder. J'avais pensé à faire ça à coup de strpos
à la place du strcspn, mais c'est vraiment tordu à suivre comme algo.

  En fait, le parser actuel utilise une méthode complètement
différente : on isole les bornes d'un bloc (une boucle en l'occurence)
puis on parse l'intérieur de chaque sous bloc. Ma version avance dans le
code, empile les blocs et les dépile quand il rencontre des marqueurs de
fin. C'est vrai que c'est pas super adapté à ce type de syntaxe, mais
je pense que ton idée buterait sur les mêmes problèmes que moi.
  En effet, on ne peut chercher un <B* que quand on connait le nom
du <BOUCLE associé. on mélange donc complètement les étapes lexicales
et syntaxiques.
  La seule alternative, c'est de faire du vrai lex/yacc, mais on
passerait un temps fou à desosser du html pour rien.

De plus rien n'empêche
de les optimiser (voir par exemple utilisation de strpos
dans la détection de boucles du parser actuel), ce qui
est beaucoup plus difficile avec une abstraction du
matching dans la grammaire "générique".

Désormais, je génère un code qui fait un peu ça :
- strcspn pour trouver un caractère interessant
- si c'est un prefixe de regex, strcmp sur le début des regex pour
  filtrer, puis ereg si ça ressemble à un truc bien.
  Du coup, on n'appelle plus 6 ereg différents pour chaque tag html.

  Sinon, y a t'il des gens très balaises en php ici ? c'est pour
savoir comment on pourrait optimiser le code en évitant les substr
à tour de bras notamment. Ma version est tout le temps obligée de
tailler des strings en tranches alors qu'il faudrait pas grand
chose pour l'éviter, mais j'sais pas trop comment php gère les
strings en interne.

À+, Pif.

C'est arrivé ...
- http://piif.free.fr/tmp/PLACID.tgz : le code spécifique au générateur
  de parseur. Il contient notamment 2 grammaires pour reconnaitre du
  pseudo xml, comme exemple. Reste à documenter tout ça.

- http://piif.free.fr/tmp/parser.tgz : la partie spip, dont une version
  modifiée de inc-calcul-squel.php3 tiré de la version cvs du ... 26/09
  je crois.

Pour utiliser ça :
- utiliser une 1.7a7
- mettre de coté le inc-calcul-squel.php
- détarrer les 2 tar dans le répertoire spip (y'aura des fichiers en trop
  mais on n'en est pas encore au nettoyage final :wink:
- that's all.

  Tant que je suis dans ce thème là, je me suis demandé un truc à propos
de la génération de code : les CACHE/skel_* contiennent des fonctions
qui font des tas de $html.=" le html du squelette " pour retourner $html
en vrac à la fin.
  Pourquoi ne pas faire des echo directement pour le dynamique et des
?>le html<? pour le statique ?
  Ça serait vacheùement plus léger, mais je suppose qu'il y a un truc
qui fait qu'on peut pas ?

À+, Muzo .. heu .. Pif.

PS : le concours de l'acronyme qui va bien pour p.l.a.c.i.d. est ouvert :wink:

Christian Lefebvre wrote:

Pourquoi ne pas faire des echo directement pour le dynamique et des
?>le html<? pour le statique ?
Ça serait vacheùement plus léger, mais je suppose qu'il y a un truc
qui fait qu'on peut pas ?

quand tu veux faire de l'objet, tu ne peux pas traiter aussi facilement des methodes qui font du print.
alors que si tu dispose du resultat d'une methode dans une string, tu peux tout faire ex :

function test_me($string){
  $string = '1234';
  $string = $this->filtre2($this->filtre1($string);
  return $string
}

vas-y faire la meme chose si ce sont des print.
j'espere que ca repond un peu a ta question sans te froisser.

ps: j'aipas bien saisi comment ca fonctionne placide, ou alors ca fonctionne a la perfection et
je n'ai rien vu sur mon site. Mais je n'ai pas réussi a voir mes propres squelettes. C'est dans doute
normal. Du coup c'est bleufant parce que ca marche nickel !!!

Christian Lefebvre wrote:

J'ai un peu profité de ces congés pour avancer sur mon parser
Je pose ça dans un coin d'ici peu.

C'est arrivé ...
- http://piif.free.fr/tmp/PLACID.tgz : le code spécifique au générateur
  de parseur. Il contient notamment 2 grammaires pour reconnaitre du
  pseudo xml, comme exemple. Reste à documenter tout ça.

- http://piif.free.fr/tmp/parser.tgz : la partie spip, dont une version
  modifiée de inc-calcul-squel.php3 tiré de la version cvs du ... 26/09
  je crois.

Pour utiliser ça :
- utiliser une 1.7a7
- mettre de coté le inc-calcul-squel.php
- détarrer les 2 tar dans le répertoire spip (y'aura des fichiers en trop
  mais on n'en est pas encore au nettoyage final :wink:
- that's all.

voila, je l'ai installé sur mon environnement pro ; ben j'ai qu'une chose à
dire : c'est saisissant. Globalement il n'y a pas de différence, sauf étrangement
dans l'affichage de l'introduction des articles (peut-etre une fonction nettoie*).

pour voir ce que ca donne (en théorie, parce que placid n'est pas installé la-bas)
voir http://villeneuve-tolosane.net/

je fais bcp d'include, des appels de fonctions pour les boites, sinon du SPIP
tout a fait standard. Je navigue sur les différentes pages et pas de différences
notables.

bravo Christian ! joli travail !

Christian Lefebvre wrote:

Pour utiliser ça :
- utiliser une 1.7a7
- mettre de coté le inc-calcul-squel.php
- détarrer les 2 tar dans le répertoire spip (y'aura des fichiers en trop
  mais on n'en est pas encore au nettoyage final :wink:
- that's all.

pour tester il faut aussi ca :

   $parser_version="pif";

quelque part dans le code d'appel.

snifff, c'est pas placid que j'ai vu tourner si joliment :wink:
(décidement)

Christian Lefebvre wrote:

J'ai un peu profité de ces congés pour avancer sur mon parser
Je pose ça dans un coin d'ici peu.

C'est arrivé ...
- http://piif.free.fr/tmp/PLACID.tgz : le code spécifique au générateur
  de parseur. Il contient notamment 2 grammaires pour reconnaitre du
  pseudo xml, comme exemple. Reste à documenter tout ça.

- http://piif.free.fr/tmp/parser.tgz : la partie spip, dont une version
  modifiée de inc-calcul-squel.php3 tiré de la version cvs du ... 26/09
  je crois.

hier soir, j'ai jeté un oeil au parseur Smarty qui est supposé etre une référence.
J'ai vu qu'ils faisaient surtout des preg_* (match, replace). Il faut
dire qu'ils ont un avantage certain sur SPIP : les caracteres principaux
de délimitations sont { } plutot que <> comme et du coup ; nous devons lutter
contre les tags HTML ; j'imagine le coup que ca peut apporter.

D'un autre coté, les expressions regulieres se déroulent non pas en php,
mais en langage natif ; du coup elle sont propices a une plus grande vélocité.

concernant les expressions régulieres de PLACID, elles sont franchement tres
complexes, donc certainement assez lourdes a traiter. Enfin faudrait encore faire
des mesures pour etre certain.

typiquement :

if(preg_match('/<BOUCLE\(...\).*>/') // au plus simple
if(preg_match('/<BOUCLE\(...\)(.*)>/') // avec variable ()
if(preg_match('/<BOUCLE_(.*?)\(...\)(.*)>/') // avec variable ()

celle de Christian :slight_smile: "<BOUCLE([0-9]+|[-_][-_.a-zA-Z0-9]*)[[:space:]]*\(([^)]*)\)([[:space:]]*\{([^}]*(\}[[:space:]]*\{[^}]*)*)\})?[[:space:]]*>"

heuu, y'a pas qq pour m'expliquer :wink: enfin non, ca change rien.

en fait faudrait trouver le juste milieu entre ER riche qui retourne l'intégralité
des infos et celle qui permet d'attrapper les symboles. (suppositions)

PS: placid bloque, sans retour aucun ;-( pas de message, rien !

Salut,

concernant les expressions régulieres de PLACID, elles sont franchement tres
complexes, donc certainement assez lourdes a traiter. Enfin faudrait encore faire
des mesures pour etre certain.
typiquement :
if(preg_match('/<BOUCLE\(...\).*>/') // au plus simple
celle de Christian :slight_smile:
"<BOUCLE([0-9]+|[-_][-_.a-zA-Z0-9]*)[[:space:]]*\(([^)]*)\)([[:space:]]*\{([^}]*(\}[[:space:]]*\{[^}]*)*)\})?[[:space:]]*>"

Dans beacoup de cas, il peut etre interessant de parser avec une regexp
hyper simple (donc rapide) quite à repasser une expression plus complexe
seulement quand ca tilt, pour obtenir tous les arguments, non ?

Par exemple, ici, si on défile 500 lignes pour trouver 6 fois <BOUCLE ...>
ou </BOUCLE> il vaut peut etre mieux faire un simple string matching 500
fois puis 6 regexpr complexes (soit 506 opérations dont 6 sont
lentes) plutot que 'seulement' 500 opération regexpr complexe...

  Yannick

Yannick Patois wrote:

Dans beacoup de cas, il peut etre interessant de parser avec une regexp
hyper simple (donc rapide) quite à repasser une expression plus complexe
seulement quand ca tilt, pour obtenir tous les arguments, non ?

Par exemple, ici, si on défile 500 lignes pour trouver 6 fois <BOUCLE ...>
ou </BOUCLE> il vaut peut etre mieux faire un simple string matching 500
fois puis 6 regexpr complexes (soit 506 opérations dont 6 sont
lentes) plutot que 'seulement' 500 opération regexpr complexe...

c'est exactement là ou je voulais en venir, mais tout cela doit etre mesurable.

C'est ce qui est fait. Le code généré
- cherche des <, (, # ...
- pour les <
  - cherche des BOUCLE, /BOUCLE ...
  - pour les BOUCLE, cherche la regex de la mort

À+, Pif.

Laissez tomber cette remarque, j'ai dit n'importe quoi : à ce moment
là, on balance pas le code à l'internaute, mais dans le fichier de cache
=> impossible à faire autrement, à moins qu'il existe un mins de
détourner la sortie ? (à la façon du divert en m4)

À+, Pif.

et m... exact, j'ai oublié ... c'était un truc pour tester les
2 versions ...

À+, Pif.

Christian Lefebvre wrote:

Laissez tomber cette remarque, j'ai dit n'importe quoi : à ce moment
là, on balance pas le code à l'internaute, mais dans le fichier de cache
=> impossible à faire autrement, à moins qu'il existe un mins de
détourner la sortie ? (à la façon du divert en m4)

oui, ob_start, ob_stop, ob_get_content ; mais faut voir l'impact sur les perfos.

Christian Lefebvre wrote:

C'est ce qui est fait. Le code généré - cherche des <, (, # ...
- pour les <
- cherche des BOUCLE, /BOUCLE ...
- pour les BOUCLE, cherche la regex de la mort

ce qu'il est fort !

c'est donc a ca qu'il sert le prefix de tes tokens :wink: heu, ben en fait je m'en doutais
mais je suis pas aller voir le code trop en detail.

Christian Lefebvre wrote:

>C'est ce qui est fait. Le code généré
>- cherche des <, (, # ...
>- pour les <
> - cherche des BOUCLE, /BOUCLE ...
> - pour les BOUCLE, cherche la regex de la mort
>
ce qu'il est fort !

mersssi :wink:

c'est donc a ca qu'il sert le prefix de tes tokens :wink:

  Ouaip, c'était pour me simplifier la vie, mais maintenant que c'est
codé, j'me dis qu'on pourrait le trouver automatiquement, et éviter
d'obliger à citer le prefixe dans le fichier de grammaire.
  À voir pour la v2.

À+, Pif.