Short: Improved RegExps, using PCRE
From: Lars, Fini
Date: 20010406
State: New

> Mhm, ich kenne die Perl REs zwar nicht so gut (willst du nur die 
> Funktionalitaet in efuns verpackt, oder auch Sprachaenderungen); aber gerade 
> gestern habe ich die PCRE ("Perl Compatible Regular Expressions") Library 
> gefunden, was eine zukuenftige Implementierung erheblich vereinfacht.

Aehm, ich wollte nur dass man bei den vorhandenen efuns auch
'vernuenftige' RE benutzen kann. Was mir zb extrem fehlt ist ein 'non
greedy *' Operator, also der beliebig viele, jedoch so wenig wie moeglich
Wiederholungen der vor RE nimmt. In Perl macht man das mit einem
angehaengten '?'. Mal sehen ob ich ein Beispiel finde...

      $skill = $1   if ( /"skills":(.*?]\))/ );

Das Perlskript liest ein LPC .o-File ein. Dort gibt es ein Mapping
(properties), welches mit dem Schluessel 'skills' ein Mapping hat, was die
Skills repraesentiert. Ich will also beliebige Zeichen haben bis zum
ersten Auftreten von ']'. In diesem Fall koennte man auch schreiben
[^\]]* aber es gibt andere Faelle (zb kann man mit REs sehr schoen Strings
aus Savefiles rausholen, unter Beachtung von gequoteten " usw, da geht das
nicht oder nur mit erheblichem Aufwand. (Ganz unten haenge ich dir maln
Beispiel ran, was die \" beachtet und so weiter und '?' nutzt)

Also wenn die reg*(E) das (auch) nehmen wuerden waer das schon ganz schoen
klasse. Wenn man da das PCRE einbinden koennte (muesste man bestimmt
wieder einen neuen Schalter einbauen, welche Art von Regexps man denn will
grumpf)...

Aenderungen an efuns, da faellt mir ein, ein regexplode welches nicht die
delimiter mitliefert waer auch bequem (analog Perls split, dort kann man
beides haben wie man will). Schoen waer in dem Zusammenhang auch ein
'Suche in dem String str nach Regexp RE und liefer mir das'. ZZ ist das
sehr aufwenidig in einem String was zu suchen und das dann zu benutzen.
Natuerlich kann ich strs = regexplode(str, RE); machen und dann die
Groesse von strs pruefen und mein Zielstring ist dann strs[1]. ;o)
Aber ziel = xxx(str, RE); (Irgendwie scheint mir das zu keiner vorhandenen
Efun zu passen).


Knuddels und Gruss aus dem verregneten Hamburg

  Fini


----
Wieder eine Property. Im Mapping ist hinter dem Schluessel 'explored' ein
LPC-Bitstring. Wenn man den komplett haben will muss man wissen wo er
zuende ist, also schoen alle " zaehlen und dabei \" ignorieren.

      $explo = $1   if ( /"explored":"(.*?[^\\]+?(\\\\)*?)"/ );

Ich glaub irgendwann muss ich maln Perl Package schreiben was einfach
jedes .o File einlesen kann und ein entsprechendes Mapping (hash)
zurueckliefert in dem alle Vars dann abgebildet werden *grueble* ich
schick dir das dann ;o) Mal sehen ob beim Driver die Docu zu den Savefiles
schon gut genug ist ;o))

----
Zum Regex Kram fiel mir noch ein, dass es schoen waere, wenn man regexp()
auch ohne Array benutzen kann, wenn man nur sehen will ob ein String passt
muss man den zZ via sizeof(regexp(({str}), re)) testen. Je mehr ich
darueber nachdenke, desdo weniger trivial erscheint es mit mit den
PerlREs. Die koennen in Perl naemlich verschiedene Modi haben, die man nun
eigentlich mituebergeben koennen muesste. Ausserdem sollte man auch auf
die Klammerausdruecke zugreifen koennen, dort waere am besten irgendwie
sowas...

  n_regexp(string str, string RE, string parameter).

Wenn 'parameter' nicht gegeben ist, wird der normale endliche Automat des
Muds benutzt. Sonst wird halt der Perl-Automat benutzt, der die Parameter
erhaelt. Wenn die RE gefunden wird lieferts 'wahr' sonst 'unwahr'.
Weiterhin (und deswegen wuerde ich die Efuns trennen dh anders nennen)
waere eine Unterstuertzung fuer die Rueckwaertsreferenzen klasse. Also
was man bei regreplace mit \1 usw erhalten kann. Ich traeume mir vor, dass
ich an n_regexp() nun auch angeben kann, welche Referenzen mich
interessieren und die liefert es dann als Array. (Da das doch sehr anders
ist als regexp() eine andere Efun.)
Also parameter = "ig\1\5\9" oder so ('\'s sollen "\\" sein ;o).


Btw kann man den (nichtdeterministischen) Automaten mit einer Zeile in die
ewigen Jagdgruende schicken ('regular expressions' von oreilly entnommen):

 regexp(({"=XX============================================"}), "X(.+)+X")

Eventuell sollte man in dem FA halt einfach jedem Backtracking eine
eval-cost zuordnen um das zu verhindern (die Laufzeit des Matchens oben
ist propotional pow(2, strlen)). Das waere zB moeglich bei Zeile 1158 des
regexp.c:
                        return MY_TRUE;
                /* Couldn't or didn't -- back up. */
                no--;
+               evals += 5;
+               if (evals > evals_max) regerror("too many backtracks");
                reginput = save + no;
            }

(mit evals und evals_max symbolisch fuer die Kosten). Ich schlage gerade
vor genau DAS als Kriterium zu nehmen, weil das ziemlich sicher soetwas
unendliches erkennt, alles andere kann auch 'viel' werden, aber da kanns
trotzdem gleich fertig sein.
----------

Hmm, hatte heute ein wenig Zeit und hab mir mal das PCRE Paket
angeschaut. Das ist ja wirklich supereinfach zu benutzen. Konnte dann dem
nicht wiederstehen, es gleich mal in den Driver reinzufummeln. Das
Resultat hab ich dir mal als diff angehaengt.

Es funktioniert alles genau so wie ich es mir ertraeume ;o) naja, fast.
Auf jeden Fall so aehnlich wie ichs in den Mails an dich beschrieb.
Was bei dem Diff absolut fehlt ist eine 'richtige' Integration. Sprich man
muss das Paket sich selbst holen und die Bibliothek erzeugen. Ausserdem
sind noch feste Pfade drin, halt die meiner Testumgebung. Weitere
Aenderungen die ich am Makefile vorgenommen hab unten.

Ich hab keine Ahnung ob dir der Code gefaellt, und ich muss auch sagen ich
hab noch nie was am Driver gemacht, keine Ahnung zB ob ich irgendwo ein
Speicherloch eingenaut hab (wegen Strings zB). Da das alles ueber xalloc()
laeuft bin ich aber guter Hoffnung.

Wenn du meinst, dass ich auf dem richtigen Wege bin, kann ich da gerne
weitermachen; das 3. Argument haette ich gerne optional, die Manpage fehlt
auch noch, es kostet keine evals, die compilierten Regexs werden nicht
gebuffert und so weiter und so fort.

Ich hoffe das bringt den Driver einen Schritt naeher an 'richtige' Regexs.


Gruesse aus dem inzwischen dunklen Hamburg,

   Fini


----

Zum PCRE-Paket: Also erstmal haben die sonne komische Licence, die
teilweise die GPL ueberdecken koennte (ich glaube der Driver ist unter
GPL, oder? *gg*). Da muesste dann mal wer schauen, ob man das Paket mit in
die Dist packen darf oder nicht. Letztere waere natuerlich aeusserst
laestig.

Gut. Dann zum Testen hab ich das ganze statisch gelinkt gehabt, weil ich
compiliere und teste auf verschiedenen Systemen. Das funzte auch soweit
ganz gut. Da ich keinerlei Rechte auf dem Compilier-Rechner hab, hatte ich
den Pcre-Krempel in einem Nachbarverzeichnis liegen. In dem Makefile hab
ich dann das Dazulinken aktiviert durch
LIBS=-lm  -lcrypt -lpcre -L/mud/src/pcre-3.4/ -static
wenn das 'richtig' in die Driver-Dist kommt muesste das natuerlich alles
automagisch gehen ;o)

Btw den Automaten kann man (selbstverstaendlich) mit derselben Regex von
letztens in eine 'unendliche' Schleife schicken. Wie man dort dann
nachtraeglich Evalkosten pro Backtrack einbaut *gruebel*...
Uebrigens ist der Pcre-Automat etwa doppelt so schnell bei der Schleife,
20 '=' nach den XX dauern bei mir 2 bzw 4 Sekunden.
---------------------------------------------------------------------------
Hallo Mateese!

Hmm, hatte heute ein wenig Zeit und hab mir mal das PCRE Paket
angeschaut. Das ist ja wirklich supereinfach zu benutzen. Konnte dann dem
nicht wiederstehen, es gleich mal in den Driver reinzufummeln. Das
Resultat hab ich dir mal als diff angehaengt.

Es funktioniert alles genau so wie ich es mir ertraeume ;o) naja, fast.
Auf jeden Fall so aehnlich wie ichs in den Mails an dich beschrieb.
Was bei dem Diff absolut fehlt ist eine 'richtige' Integration. Sprich man
muss das Paket sich selbst holen und die Bibliothek erzeugen. Ausserdem
sind noch feste Pfade drin, halt die meiner Testumgebung. Weitere
Aenderungen die ich am Makefile vorgenommen hab unten.

Ich hab keine Ahnung ob dir der Code gefaellt, und ich muss auch sagen ich
hab noch nie was am Driver gemacht, keine Ahnung zB ob ich irgendwo ein
Speicherloch eingenaut hab (wegen Strings zB). Da das alles ueber xalloc()
laeuft bin ich aber guter Hoffnung.

Wenn du meinst, dass ich auf dem richtigen Wege bin, kann ich da gerne
weitermachen; das 3. Argument haette ich gerne optional, die Manpage fehlt
auch noch, es kostet keine evals, die compilierten Regexs werden nicht
gebuffert und so weiter und so fort.

Ich hoffe das bringt den Driver einen Schritt naeher an 'richtige' Regexs.


Gruesse aus dem inzwischen dunklen Hamburg,

   Fini


----

Zum PCRE-Paket: Also erstmal haben die sonne komische Licence, die
teilweise die GPL ueberdecken koennte (ich glaube der Driver ist unter
GPL, oder? *gg*). Da muesste dann mal wer schauen, ob man das Paket mit in
die Dist packen darf oder nicht. Letztere waere natuerlich aeusserst
laestig.

Gut. Dann zum Testen hab ich das ganze statisch gelinkt gehabt, weil ich
compiliere und teste auf verschiedenen Systemen. Das funzte auch soweit
ganz gut. Da ich keinerlei Rechte auf dem Compilier-Rechner hab, hatte ich
den Pcre-Krempel in einem Nachbarverzeichnis liegen. In dem Makefile hab
ich dann das Dazulinken aktiviert durch
LIBS=-lm  -lcrypt -lpcre -L/mud/src/pcre-3.4/ -static
wenn das 'richtig' in die Driver-Dist kommt muesste das natuerlich alles
automagisch gehen ;o)

Btw den Automaten kann man (selbstverstaendlich) mit derselben Regex von
letztens in eine 'unendliche' Schleife schicken. Wie man dort dann
nachtraeglich Evalkosten pro Backtrack einbaut *gruebel*...
Uebrigens ist der Pcre-Automat etwa doppelt so schnell bei der Schleife,
20 '=' nach den XX dauern bei mir 2 bzw 4 Sekunden.

---------------------------------------------------------------------------
Hallo Mateese!

Hab grad nochmal ein bissl Zeit gefunden und hab mal geschaut ob ich
den rxcache nicht irgendwie mitbenutzen kann. So schwierig scheint das
nicht zu sein, ich muesste nur den Wert von from_ed irgendwie
erweitern, dass man pcre-Ausdruecke erkennen kann.

Am eigentlichen Code habe ich nicht viel getan, ausser ihn nach array.c
zu verschieben, was mE aber auch kein besonders schoener Platz ist.
Immerhin sind dort mehr verwandet Funktionen als im backend.c.
Irgendwie funzt nun auch dynamisches linken.

Dann sollte man natuerlich auch in den anderen reg* Funktionen die
perlaehnlichen Ausdruecke verwenden koennen. 'normale' sollten einfach
so zu ersetzen sein, die excompat muesste man ein bissl vorher
anpassen - wenn man einfach fuer alles pcre nehmen will. Ich denke das
ist der einfachste Ansatz anstatt zwei Automaten parallel zu nutzen.
Was sagst du dazu.

Hmm, ich sehe gerade einen Fehler im rxcache *g*:
regreplace("1(ab)", "1\\(ab\\)*", "AAA", 0)  ->  "AAA"
regreplace("1(ab)", "1\\(ab\\)*", "AAA", 2)  ->  "AAA"

Das 2. Ergebnis ist falsch, es wird einfach die nicht-ex-Regex aus
dem Cache genommen. Das richtige Ergebnis waere:
regreplace("2(ab)", "2\\(ab\\)*", "AAA", 2)  ->  "AAA(ab)"

Hmm, stimmt, regcomp_cache() prueft nur ob from_ed uebereinstimmt, auf
excompat wird gar keine Ruecksicht genommen, in regexp ist ja auch
gar kein Datenfeld dafuer vorgesehen. Man koennte dort from_ed als
allgemeines Flag nehmen, oder *grins* steigen wir gleich um auf
pcre? Naja, ist wohl noch nie jemandem aufgefallen, ich wuesste aber
auch nicht wer den bloeden ex-Modus ueberhaupt nutzen wollen sollte ;)

Ach, eine Hilfeseite hab ich mal angefangen, damit du weisst was
ueberhaupt die Efun macht.

Ich wuerde also gerne wissen, in welcher Richtung es weitergehen soll. Wie
ich es am liebsten haette weisst du ja, ich wuerde den rxcache umschreiben
und die alten regexs rauswerfen, und diese pcregexp(E) irgendwie sinnvoll
umbennenen oder evt mit in regexp(E) legen (wenn Arg 1 kein Array ist),
aber das ist dann alles wieder so unuebersichtlich fuer Magier. Weiterhin
hab ich keine Ahnung was der Unterscheid von TEFUN usw ist (nur sehr
grob), was fuer ein Typus es werden soll muesstest du sagen. Das Einbinden
vom Pcre-Paket weiss ich auch nicht wie das geht. Also von Hand schon,
aber wie soll es beim Driver sein? Auch 'extern' builden und dynamisch
linken oder besser den Code mitreinziehen? Aber was ist mit dem configure
des Paketes, da muss ja auch abhaengiger Code sein usw usf... Seufz.


Nagut. Das wars erstmal aus Hamburg,

   Fini

---------------------------------------------------------------------------
Hallo Mateese!

Was auch schoen waere zu haben, waere wenn der 3. Parameter von
regreplace() auch eine Closure sein koennte. Dort steht jetzt
der Ersatzstring. Wenn es eine Closure ist, so sollte diese den
Originaltext bekommen und den Ersatztext zurueckliefern.

Das ist besonders dann nuetzlich, wenn man mit etwas 'nichtstatischem'
ersetzen will. Zz muss man dazu mit regexplode() den String zerteilen
und dann mit 2er Schritten durchgehen und ersetzen.

Beispiel:
Eine Funktion mixed_to_string(), die alles in ein lesbares Format
wandelt (ist eine simul_efun bei uns, ist mE lesbarer als %O).
Nun moechte ich alle Zeichen des Strings, die kleiner als ' ' sind
durch ihre Kuerzel ersetzen. Bei \n \t usw kein Problem. Doch was
ist mit beliebigen anderen Sequenzen? (%O liefert dort einfach NIX,
nicht sehr nuetzlich).

regreplace(str, "[^ -~]", (: sprintf("\\%03d", $1[0]) :), 1);

wuerde das sehr 'elegant' loesen. Zur Zeit ist unser Konstrukt dort
eher unuebersichtlich:

mix = regexplode(mix, "[^ -~]");
for (i = sizeof(mix)-2; i>0; i-=2) 
  mix[i] = sprintf("\\%03d", mix[i][0]);
mix = implode(mix, "");

----

Fuer die bislang pcregexp() genannte Funktion wuerde ich uebrigens
regmatch() vorschlagen als Namen, das passt besser in die vorhandenen
reg*() Namen rein - und nach meinen Vorstellungen sollen ja (optional)
alle reg* mit pcre laufen, so ist die Unterscheidung im Funnamen
ueberfluessig.


Cheers,

  Fini


