ugrás a tartalomhoz

Php rekurzív regex

inf · 2009. Aug. 22. (Szo), 15.16
Üdv.

Van egy ilyenem:

$pattern='%(?<string>.*?){(?<function>\w+)(?:}(?<block>(?R)*?.*?){/(?P=function))?}%sD';
$test=
'text1
{test}
 d1 begin
 {test}
   d2 begin
   {test}
   d2 end
 {/test}
 d1 end
{/test}
text2
{test}
 d1b begin,end
{/test}';
preg_match_all($pattern,$test,$matches,PREG_SET_ORDER);
var_dump($matches);
aminek rossz a kimenete:

array (
  0 =>
  array (
    0 => 'text1{test}d1 begin{test}d2 begin{test}d2 end{/test}',
    'string' => 'text1',
    'function' => 'test',
    'block' => 'd1 begin{test}d2 begin{test}d2 end',
  ),
  1 =>
  array (
    0 => 'd1 end{/test}text2{test}d1b begin,end{/test}',
    'string' => 'd1 end{/test}text2',
    'function' => 'test',
    'block' => 'd1b begin,end',
  ),
)
helyette ilyet szeretnék:

array (
  0 =>
  array (
    0 => 'text1{test}d1 begin{test}d2 begin{test}d2 end{/test}d1 end{/test}',
    'string' => 'text1',
    'function' => 'test',
    'block' => 'd1 begin{test}d2 begin{test}d2 end{/test}d1 end',
  ),
  1 =>
  array (
    0 => 'text2{test}d1b begin,end{/test}',
    'string' => 'text2',
    'function' => 'test',
    'block' => 'd1b begin,end',
  ),
)
Már tanácstalan vagyok.


A lényeg, hogy a functionnek vagy van blokkja, vagy nincs, ha nincsen neki, akkor is function néven szeretném megkapni, és nyilván ha van blokk záró tag, akkor van neki blokkja. A fenti esetben ez sajnos nem teljesül.
Különböző nevű functionöknél nincs vele gond, de azonos nevűektől kiakad.
 
1

Ertelmetlen

janoszen · 2009. Aug. 22. (Szo), 23.07
Smallscreen keszuldken vagyok, ugyhogy nem olvastam el a kodot. Ha jol emlekszem a formalis nyelvek orakra, a regularis nyelvek halmaza nem fedi le az altalad ohajtott nyelvet. Ergo regularis illesztessel nem tudod elemezni (kiveve ha hackelsz). Ha nem akarsz teljes nyelvi elemzot irni (ami egyebkent a korrekt megoldas lenne) akkor bontsd tokenere regexpekkel, majd ezt dolgozd fel. (Poor man's lexical analyzer. :) ) Csak vigyazz a secholeokra.
2

Rosszul tudod

inf · 2009. Aug. 23. (V), 12.23
Ezt rosszul tudod. PCRE támogatja a rekurzív regexet, amivel lefedhető "az általam óhajtott nyelv". Persze ezt például Smarty is rosszul tudja, ezért lehet így legalább 10x gyorsabb compilert írni az övékénél. Az igazi problémám az, hogy annyiszor agyaltam már ezen a regexen, hogy nincs kedvem hozzányúlni :D
Ha a blokkos változatot és a blokk nélkülit külön szedném, és a blokkosat raknám elsődlegesnek, akkor rendesen működne. Ami miatt a mostani formát választottam, az az, hogy így ugyanúgy function nevet kap a függvény neve.
Ha jól emlékszem van valami megoldás arra, hogy egy elfogó csoport nevet több regex rész használhasson. Majd utánanézek.

Amiben igazad van az az, hogy konkrétan ezt a nyelvet nem lehet lefedni vele, ha viszont az egysoros(blokk nélküli) dolgok megadási módját külön tudom választani a többsorosakétól(blokkal rendelkező), akkor lehetséges. Lehet, hogy elég annyi elválasztásnak, hogy jelen esetben a test az csak többsoros lehet. (Mindössze annyi a para, hogy a blokkal rendelkező függvény valamiért nincs prioritásban a blokk nélkülivel szemben. Hiába rakom őket mondjuk egy |-al elválasztott csoportba. (Ott a prioritása a legelsőnek a legnagyobb elvileg. Sajnos ez a rekurzivitásban valamiért nem érvényesül.) Gondolom mindez a regex feldolgozó működéséből adódik.)

$pattern='%(?<string>.*?)(?:{(?<function>\w+)(?:}(?<block>.*?(?:(?R).*?)*?){/(?P=function))?}|$)%sD';
$test='text1{test}d1 begin{test}d2 begin{valami}d2 end{/test}d1 end{/test}text2{test}d1b begin,end{/test}';
preg_match_all($pattern,$test,$matches,PREG_SET_ORDER);
var_dump($matches);
Például ez már tökéletesen működik. Egyelőre még gondolkodom azon, hogy ez e a jó megoldás, vagy pedig az egysoros függvénynek kéne valami külön jelölőt adni, mint mondjuk XHTML-ben a / jel a végén.

Hmm a test-re is teljesen jót ad:

$pattern='%(?<string>.*?)(?:{(?<function>\w+)(?:}(?<block>.*?(?:(?R).*?)*?){/(?P=function))?}|$)%sD';
$test='text1{test}d1 begin{test}d2 begin{test}d2 end{/test}d1 end{/test}text2{test}d1b begin,end{/test}x';
preg_match_all($pattern,$test,$matches,PREG_SET_ORDER);
var_dump($matches);

array (
  0 => 
  array (
    0 => 'text1{test}d1 begin{test}d2 begin{test}d2 end{/test}d1 end{/test}text2{test}d1b begin,end{/test}',
    'string' => 'text1',
    'function' => 'test',
    'block' => 'd1 begin{test}d2 begin{test}d2 end{/test}d1 end{/test}text2{test}d1b begin,end',
  ),
  1 => 
  array (
    0 => 'x',
    'string' => 'x',
  ),
  2 => 
  array (
    0 => '',
    'string' => '',
  ),
)
Csak máshogy gondolkodik, mint én :-) Az én gondolkodásomban a blokkosnak nem volt teljes a prioritása a blokk nélküli felett, ebben viszont van. Szóval a blokk nélküli test az a legutolsó lesz, és nem a középső. Ha megnézi az ember, így is kijönnek a nyitó és záró részek.

(Ahogy nézem Smartysok az unicode karakterekre sem gondoltak, most szoptam vele én is.)

Kipróbáltam nagyban is a fenti megoldást, aztán kiderült, hogy bugos a php pcre motorja. Szóval ehelyett a preg_match_all %...|$%-os téma helyett visszatértem a preg_split + %...%-os verzióra, mert az szépen működik (viszont lassabb). Ez van.

Nyitottam is ide egy bug reportot, ha valakit érdekel.