Short: Improved RegExps, using PCRE From: Lars, Fini Date: 20010406 State: New Ach, dann ging mein pcre-Patch nicht mehr. Ich habs also von Hand machen muessen und dabei ist mir aufgefallen, dass ich sogar in der Non-Pcre-Sektion etwas geaendert habe. Und zwar in array.c bei Zeile 2871: if (!match) { + #ifndef USE_PCRE + REGFREE(reg); + #else + pcregex_free(reg); + #endif error("Stack overflow in regexplode()"); /* NOTREACHED */ return NULL; } Das ist vielleicht eigentlich nicht noetig, aber irgendwas war da doch mit einem Refcount und so, das kommt dann wohl durcheinander. Kann mich nur dunkel an irgendson Refcount erinnern, ich glaub den brauchte man wegen rekursiven REs die kollidieren oder so *denk* ------------ > 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 ---------------------------------------------------------------------- Date: Thu, 26 Jul 2001 23:41:51 +0200 (MEST) From: Erzmagier des Wunderlandes <mud@hurrikap.rz.uni-leipzig.de> To: Lars Duening <lars@bearnip.com> Subject: Ldmud: pcre Message-ID: <Pine.LNX.4.10.10107262307100.22388-200000@hurrikap.rz.uni-leipzig.de> This message is in MIME format. The first part should be readable text, while the remaining parts are likely unreadable without MIME-aware tools. Send mail to mime@docserver.cac.washington.edu for more info. --311121419-828061240-996183711=:22388 Content-Type: text/plain; charset=us-ascii Hallo Mateese! So, da wir morgen in See stechen und ueber die Nordsee segeln gips heute schon ein diff. Zum groessten Teil ist das fertig so umgesetzt wie in der Mail besprochen. Das Diff ist eigentlich fuer 313, aber es geht auch auf 314 hab ich grad getestet. Den Code selbst habe ich nicht superdoll getestet, das kommt dann naechste Woche dran. Ich kann nur nicht wiederstehen dir schonmal zu schicken was ich hab ;o) * Der rx-Cache kann nun auch mit PCREs umgehen Beide Arten (bzw drei sind es ja jetzt) werden parallel im gleichen Hash gehalten. Um die pcres musste dazu eine Struktur gewickelt werden fuers Refcounting. Leider kann man das dann nicht mehr 'in einem Stueck' free()n, also gibt es unterschiedliche Funs je Art. Ich hatte verschiedene Datenstrukturen probiert, was besseres ist mir nicht eingefallen ohne alles umzustricken. Im rx-cache war ein kleiner Quirk, der Speicherzaehler fuer 'status' wurde auch dann schon erniedrigt, wenn der Refcount einer RE noch nicht Null war, das habe ich etwas 'linearisiert'. Weiterhin Wrapper dort eingebaut, dass PCRE auch ohne Cache laufen kann, die anderen Module benutzen nie die echten pcre-Funktionen. Jede RE wird auch ge-study()-t. Das braucht mehr Speicher und Zeit, dafuer geht das Matchen schneller. Ob das lohnt? Typische REs aus dem Wunderland haben 60 Byte 'Body' und 0 bis 40, meist 15 Byte 'Study'. count_rxcache_refs() benutzt nicht mehr count_rxcache_ref(), da man dann die Typen nicht mehr unterscheiden koennte. Sind eh nur zwei Zeilen gewesen. Die letztere Funktion benutzt nur noch ed der nur alte REs benutzt. Den Strukt der Cache-Elemente hab ich auch mal ins .h gelegt. * Main initialisiert die Benutzung der PCRE * Array erhaelt erhaelt Erweiterungen und zwar die regmatch(E) die nur da ist wenn PCRE 'da' ist. Die Efuns regexp() und regexplode() benutzen auch PCRE. * Lexer hat predefined macro __PCRE__ * (nicht im Diff) in config.h muss einfach USE_PCRE definiert werden um den ganzen Krempel anzuschalten. * (nicht im Diff) Es muessen folgende Dateien von pcre dazugelinkt werden. Ich make-e nochmal das pcre-Paket ohne Install und linke pcre.o - Hauptroutinen study.o - Optimiert die REs get.o - Hilft Submatches extrahieren (kleine Hilfsfuns) maketables.o - Erstellt die Char-Tables wie besprochen, kann man weglassen wenn keine Locale-Aenderung zur Laufzeit Des weiteren muss in rxcache.h evt der Pfad zu pcre.h angepasst werden, der bei mir in ./pcre-3.4 liegt. * Was noch fehlt: Eval-Costs und die regreplace() Funktion. Die ist jedoch dermassen naja, die wuerde ich zumindest fuer pcre komplett neu schreiben wollen. inter_sp evt an einigen Stellen setzen. So. Vielleicht magst du ja mal reinschauen. Auf jeden Fall ein schoenes (sonniges) Wochenende. Fini --311121419-828061240-996183711=:22388 diff -c /mud/src/driver/3-2-dev/src/array.c ./array.c *** /mud/src/driver/3-2-dev/src/array.c Thu Jul 26 22:47:45 2001 --- ./array.c Thu Jul 26 22:54:44 2001 *************** *** 2614,2620 **** --- 2614,2626 ---- */ { + #ifndef USE_PCRE struct regexp *reg; /* compiled regexp */ + #else + pcregex* reg; /* compiled perl regex */ + int found; + int ergs[OVEC_LEN]; + #endif CBool *res; /* res[i] true -> v[i] matches */ mp_int num_match, v_size; /* Number of matches, size of <v> */ vector_t *ret; /* The result vector */ *************** *** 2625,2633 **** --- 2631,2643 ---- return allocate_array(0); /* Compile the regexp (or take it from the cache) */ + #ifndef USE_PCRE reg = REGCOMP((unsigned char *)pattern, 0, MY_FALSE); if (reg == NULL) return NULL; + #else + reg = pcregex_cache(pattern, 0); + #endif /* Check every string in <v> if it matches and set res[] * accordingly. *************** *** 2635,2641 **** --- 2645,2655 ---- res = alloca(v_size * sizeof(*res)); if (!res) { + #ifndef USE_PCRE REGFREE(reg); + #else + pcregex_free(reg); + #endif error("Stack overflow in regexp()"); /* NOTREACHED */ return NULL; *************** *** 2651,2658 **** --- 2665,2685 ---- eval_cost++; line = v->item[i].u.string; + #ifndef USE_PCRE if (regexec(reg, line, line) == 0) continue; + #else + found = pcre_exec(reg->pProg, reg->pHints, line, strlen(line), + 0, 0, ergs, OVEC_LEN); + + if (found == PCRE_ERROR_NOMATCH) continue; + if (found <= 0) { + pcregex_free(reg); + error("regexp: %s\n", pcre_error_message(found)); + /* NOTREACHED */ + return NULL; + } + #endif res[i] = MY_TRUE; num_match++; *************** *** 2667,2673 **** --- 2694,2704 ---- num_match++; } + #ifndef USE_PCRE REGFREE(reg); + #else + pcregex_free(reg); + #endif return ret; } *************** *** 2823,2829 **** --- 2854,2867 ---- char *text; /* Input text from the vm stack */ char *pattern; /* Delimiter pattern from the vm stack */ + #ifndef USE_PCRE struct regexp *reg; /* Compiled pattern */ + #else + pcregex* reg; + int found; + int ergs[OVEC_LEN]; + size_t textlen; + #endif struct regexplode_match *matches; /* List of matches */ struct regexplode_match **matchp; /* Pointer to previous_match.next */ struct regexplode_match *match; /* Current match structure */ *************** *** 2841,2853 **** text = sp[-1].u.string; pattern = sp->u.string; reg = REGCOMP((unsigned char *)pattern, 0, MY_FALSE); ! if (reg == 0) { inter_sp = sp; error("Unrecognized search pattern"); /* NOTREACHED */ return NULL; } /* Loop over <text>, repeatedly matching it against the pattern, * until all matches have been found and recorded. --- 2879,2895 ---- text = sp[-1].u.string; pattern = sp->u.string; + #ifndef USE_PCRE reg = REGCOMP((unsigned char *)pattern, 0, MY_FALSE); ! if (reg == NULL) { inter_sp = sp; error("Unrecognized search pattern"); /* NOTREACHED */ return NULL; } + #else + reg = pcregex_cache(pattern, 0); + #endif /* Loop over <text>, repeatedly matching it against the pattern, * until all matches have been found and recorded. *************** *** 2855,2871 **** --- 2897,2937 ---- str = text; /* Remaining <text> to analyse */ num_match = 0; matchp = &matches; + #ifndef USE_PCRE while (regexec(reg, str, text)) { + #else + textlen = strlen(text); + while (PCRE_ERROR_NOMATCH != + (found=pcre_exec(reg->pProg, reg->pHints, text, textlen, + str-text, 0, ergs, OVEC_LEN))) + { + if (found <= 0) { + pcregex_free(reg); + error("regexplode: %s\n", pcre_error_message(found)); + /* NOTREACHED */ + return NULL; + } + #endif eval_cost++; match = (struct regexplode_match *)alloca(sizeof *match); if (!match) { + #ifndef USE_PCRE + REGFREE(reg); + #else + pcregex_free(reg); + #endif error("Stack overflow in regexplode()"); /* NOTREACHED */ return NULL; } + #ifndef USE_PCRE match->start = reg->startp[0]; match->end = str = reg->endp[0]; + #else + match->start = text+ergs[0]; + match->end = str = text+ergs[1]; + #endif *matchp = match; matchp = &match->next; num_match++; *************** *** 2876,2882 **** --- 2942,2952 ---- /* Prepare the result vector */ if (max_array_size && num_match > ((max_array_size-1) >> 1) ) { + #ifndef USE_PCRE REGFREE(reg); + #else + pcregex_free(reg); + #endif inter_sp = sp; error("Illegal array size"); /* NOTREACHED */ *************** *** 2915,2921 **** --- 2985,2995 ---- put_malloced_string(svp, string_copy(text)); /* Cleanup */ + #ifndef USE_PCRE REGFREE(reg); + #else + pcregex_free(reg); + #endif free_string_svalue(sp); sp--; free_string_svalue(sp); *************** *** 2924,2929 **** --- 2998,3124 ---- put_array(sp, ret); return sp; } + + /*-------------------------------------------------------------------------*/ + + #ifdef USE_PCRE + svalue_t * + f_regmatch (svalue_t *sp) + + /* TEFUN regmatch() + * + * mixed* regmatch (string text, string pattern, int opt) + * + * Match the perl compatible regex pattern on string text and return an + * array or 0 if no match could be found. + * + * The array contains the matching part of string text as first element + * and the parts matching subpatterns in the following elements. + * If a subpattern was unused a zero will be in its place in contrast to + * an empty string which could occur on used subpatterns also. + * E.g.: arr[5] is alike \5. + * + * TODO: eval costs?! + */ + + { + + char* text; + char* pattern; + int opt; + + pcregex* regex; + int ergs[OVEC_LEN]; + int found; + + vector_t* matches; + svalue_t* svp; + int i, len; + const char* match; + + /* Get the efun arguments */ + if (sp[-2].type != T_STRING) bad_xefun_arg(1, sp); + if (sp[-1].type != T_STRING) bad_xefun_arg(2, sp); + if (sp[ 0].type != T_NUMBER) bad_xefun_arg(3, sp); + + text = sp[-2].u.string; + pattern = sp[-1].u.string; + opt = sp[ 0].u.number; + + /* compile the regex */ + regex = pcregex_cache(pattern, opt); + + /* use the regex */ + /* TODO: support for pcre-options? */ + found = pcre_exec(regex->pProg, regex->pHints, text, strlen(text), + 0, 0, ergs, OVEC_LEN); + + /* no match */ + if (found == PCRE_ERROR_NOMATCH) { + pcregex_free(regex); + free_svalue(sp--); + free_string_svalue(sp--); + free_string_svalue(sp); + put_number(sp, 0); + return sp; + } + + /* error occured */ + if (found <= 0) { + inter_sp = sp; + pcregex_free(regex); + error("regmatch: %s\n", pcre_error_message(found)); + /* NOTREACHED */ + return NULL; + } + + /* create result array */ + if (max_array_size && found > (max_array_size-1)) { + pcregex_free(regex); + inter_sp = sp; + error("regmatch: result array too big"); + /* NOTREACHED */ + return NULL; + } + matches = allocate_array(found); + svp = matches->item; + + /* put resulting strings in that array */ + for (i = 0; i < found; i++) { + + if (ergs[(2*i)+1] == -1) { + /* no match stored in this subpattern */ + put_number(svp, 0); + } + else { + len = pcre_get_substring(text, ergs, found, i, &match); + if (len < 0) { + /* This should never happen because memory problems + * are handled by xalloc() */ + pcregex_free(regex); + inter_sp = sp; + error("regmatch: internal error"); + /* NOTREACHED */ + return NULL; + } + + /* memory of match was allocated via pcre_malloc == xalloc + * should it be string_copy()ed? */ + put_malloced_string(svp, (char*)match); + } + + svp++; + } + + pcregex_free(regex); + free_svalue(sp--); + free_string_svalue(sp--); + free_string_svalue(sp); + + put_array(sp, matches); + return sp; + } + #endif /* USE_PCRE */ /*-------------------------------------------------------------------------*/ svalue_t * diff -c /mud/src/driver/3-2-dev/src/func_spec ./func_spec *** /mud/src/driver/3-2-dev/src/func_spec Sat Jul 14 05:49:25 2001 --- ./func_spec Thu Jul 26 22:57:33 2001 *************** *** 512,517 **** --- 512,518 ---- string md5_encrypt(string); string *regexplode(string, string); string regreplace(string,string,closure|string,int); + mixed *regmatch(string, string, int); string trim(string, ...); string upper_case(string); diff -c /mud/src/driver/3-2-dev/src/lex.c ./lex.c *** /mud/src/driver/3-2-dev/src/lex.c Sat Jul 14 05:49:25 2001 --- ./lex.c Thu Jul 26 19:02:32 2001 *************** *** 792,797 **** --- 792,800 ---- #ifdef USE_DEPRECATED add_permanent_define("__DEPRECATED__", -1, string_copy(""), MY_FALSE); #endif + #ifdef USE_PCRE + add_permanent_define("__PCRE__", -1, string_copy(""), MY_FALSE); + #endif if (wizlist_name[0] != '\0') { if (compat_mode) diff -c /mud/src/driver/3-2-dev/src/main.c ./main.c *** /mud/src/driver/3-2-dev/src/main.c Mon Jul 16 06:36:07 2001 --- ./main.c Thu Jul 26 18:25:46 2001 *************** *** 190,195 **** --- 190,198 ---- #ifdef RXCACHE_TABLE rxcache_init(); #endif + #ifdef USE_PCRE + pcre_init(); + #endif put_number(&const0, 0); put_number(&const1, 1); diff -c /mud/src/driver/3-2-dev/src/rxcache.c ./rxcache.c *** /mud/src/driver/3-2-dev/src/rxcache.c Thu May 17 05:08:31 2001 --- ./rxcache.c Thu Jul 26 22:55:30 2001 *************** *** 54,59 **** --- 54,113 ---- #include "../mudlib/sys/debug_info.h" + #ifdef USE_PCRE + /*-------------------------------------------------------------------- + * Wrapper functions for the pcre module following below: + */ + + static uint32 pcre_malloc_size; + static const char* pcre_malloc_err; + + /* malloc wrapper for pcre package + */ + void* pcre_wrapper_malloc(size_t size) { + void* x; + xallocate(x, size, pcre_malloc_err); + if (!x) return NULL; + pcre_malloc_size += size; + return x; + } + + void pcre_wrapper_free(void *ptr) { return xfree(ptr); } + + /* Initialise the pcre memory management */ + void pcre_init(void) + { + pcre_malloc = pcre_wrapper_malloc; + pcre_free = pcre_wrapper_free; + } + + /* generate apropriate error message for pcre_exec() calls */ + const char* pcre_error_message(int found) + { + const char* text; + if (found > 0) return NULL; + switch (found) { + case 0: + text = "too many capturing parentheses"; break; + case PCRE_ERROR_NULL: + text = "code or subject NULL"; break; + case PCRE_ERROR_BADOPTION: + text = "unknown option specified"; break; + case PCRE_ERROR_BADMAGIC: + text = "regex memory invalid"; break; + case PCRE_ERROR_UNKNOWN_NODE: + text = "regex memory violated"; break; + case PCRE_ERROR_NOMEMORY: + text = "out of memory"; break; + default: + text = "unknown internal error"; break; + } + return text; + } + + #endif /* USE_PCRE */ + /*--------------------------------------------------------------------*/ + #ifdef RXCACHE_TABLE /*--------------------------------------------------------------------*/ *************** *** 66,84 **** #define RxStrHash(s) ((s) % RXCACHE_TABLE) #endif - - /* One expression hashtable entry */ - - typedef struct RxHashEntry { - unsigned char * pString; /* Generator string, a shared string - * NULL if unused */ - p_uint hString; /* Hash of pString */ - Bool from_ed; /* The from_ed value */ - Bool excompat; /* The excompat value */ - regexp * pRegexp; /* The generated regular expression from regcomp() */ - } RxHashEntry; - - /* Variables */ static RxHashEntry xtable[RXCACHE_TABLE]; /* The Expression Hashtable */ --- 120,125 ---- *************** *** 98,103 **** --- 139,260 ---- memset(xtable, 0, sizeof(xtable)); } + #ifdef USE_PCRE + pcregex* pcregex_cache(const char* expr, int opt) + { + p_uint hExpr; + pcregex *pRegexp; + RxHashEntry *pHash; + const char* pErrmsg; + int erridx; + + iNumXRequests++; + + hExpr = whashstr((char *)expr, 50); + pHash = xtable+RxStrHash(hExpr); + + /* Look for a ready-compiled regexp */ + if (pHash->pString != NULL + && pHash->hString == hExpr + && pHash->pc == 1 + && !strcmp((char *)pHash->pString, (char *)expr) + ) + { + iNumXFound++; + return pcregex_dup(pHash->pRegex.pc_re); + } + + /* Regexp not found: compile a new one and enter it + * into the table. + */ + + xallocate(pRegexp, sizeof(pcregex), "rxcache entry"); + + pcre_malloc_size = 0; + pcre_malloc_err = "compiling regex"; + pRegexp->pProg = pcre_compile(expr, opt, &pErrmsg, &erridx, NULL); + + if (NULL == pRegexp->pProg) { + xfree(pRegexp); + error("pcre: %s at offset %d\n", pErrmsg, erridx); + /* NOTREACHED */ + return NULL; + } + + pcre_malloc_err = "studying regex"; + pRegexp->pHints = pcre_study(pRegexp->pProg, 0, &pErrmsg); + + if (pErrmsg) { + xfree(pRegexp); + error("pcre: %s\n", pErrmsg); + /* NOTREACHED */ + return NULL; + } + + pRegexp->regalloc = pcre_malloc_size + sizeof(pRegexp); + pRegexp->refs = 1; + + expr = (unsigned char *)make_shared_string((char *)expr); + + if (NULL != pHash->pString) + { + iNumXCollisions++; + free_string((char *)pHash->pString); + + if (pHash->pc) pcregex_free(pHash->pRegex.pc_re); + else rx_free(pHash->pRegex.ed_re); + } + pHash->pString = (unsigned char *) expr; /* refs are transferred */ + pHash->hString = hExpr; + pHash->pRegex.pc_re = pRegexp; + pHash->from_ed = 0; /* not used */ + pHash->excompat = 0; /* not used */ + pHash->pc = 1; + + iNumXEntries++; + iXSizeAlloc += pRegexp->regalloc; + + return pcregex_dup(pRegexp); + } /* pcregex_cache */ + + /*--------------------------------------------------------------------*/ + + pcregex * + pcregex_dup (pcregex * expr) + + /* Increase the reference count of <expr> and return it. + */ + + { + expr->refs++; + return expr; + } + + /*--------------------------------------------------------------------*/ + + void + pcregex_free (pcregex * expr) + + /* Decrease the reference count of <expr>. If it reaches 0, the + * structure and all associated data is deallocated. + */ + + { + expr->refs--; + if (expr->refs) return; + + iNumXEntries--; + iXSizeAlloc -= expr->regalloc; + if (expr->pHints) xfree(expr->pHints); + xfree(expr->pProg); + xfree(expr); + } + + /*--------------------------------------------------------------------*/ + + + #endif /* USE_PCRE */ + /*--------------------------------------------------------------------*/ regexp * regcomp_cache(unsigned char * expr, Bool excompat, Bool from_ed) *************** *** 123,137 **** pHash = xtable+RxStrHash(hExpr); /* Look for a ready-compiled regexp */ ! if (pHash->pString != NULL ! && pHash->hString == hExpr ! && pHash->from_ed == from_ed && pHash->excompat == excompat && !strcmp((char *)pHash->pString, (char *)expr) ) { iNumXFound++; ! return rx_dup(pHash->pRegexp); } /* Regexp not found: compile a new one and enter it --- 280,295 ---- pHash = xtable+RxStrHash(hExpr); /* Look for a ready-compiled regexp */ ! if (pHash->pString != NULL ! && pHash->hString == hExpr ! && pHash->from_ed == from_ed ! && pHash->pc == 0 && pHash->excompat == excompat && !strcmp((char *)pHash->pString, (char *)expr) ) { iNumXFound++; ! return rx_dup(pHash->pRegex.ed_re); } /* Regexp not found: compile a new one and enter it *************** *** 147,162 **** { iNumXCollisions++; iNumXEntries--; - iXSizeAlloc -= pHash->pRegexp->regalloc; free_string((char *)pHash->pString); ! rx_free(pHash->pRegexp); } ! pHash->pString = expr; /* refs are transferred */ ! pHash->hString = hExpr; ! pHash->pRegexp = pRegexp; ! pHash->from_ed = from_ed; ! pHash->excompat = excompat; iNumXEntries++; iXSizeAlloc += pRegexp->regalloc; --- 305,321 ---- { iNumXCollisions++; iNumXEntries--; free_string((char *)pHash->pString); ! if (pHash->pc) pcregex_free(pHash->pRegex.pc_re); ! else rx_free(pHash->pRegex.ed_re); } ! pHash->pString = expr; /* refs are transferred */ ! pHash->hString = hExpr; ! pHash->pRegex.ed_re = pRegexp; ! pHash->from_ed = from_ed; ! pHash->excompat = excompat; ! pHash->pc = 0; iNumXEntries++; iXSizeAlloc += pRegexp->regalloc; *************** *** 255,262 **** { expr->refs--; ! if (!expr->refs) ! xfree(expr); } /*--------------------------------------------------------------------*/ --- 414,424 ---- { expr->refs--; ! if (expr->refs) return; ! ! iNumXEntries--; ! iXSizeAlloc -= expr->regalloc; ! xfree(expr); } /*--------------------------------------------------------------------*/ *************** *** 276,282 **** for (i = 0; i < RXCACHE_TABLE; i++) if (NULL != xtable[i].pString) ! xtable[i].pRegexp->refs = 0; } /* clear_rxcache_refs() */ /*--------------------------------------------------------------------*/ --- 438,447 ---- for (i = 0; i < RXCACHE_TABLE; i++) if (NULL != xtable[i].pString) ! { ! if (xtable[i].pc) xtable[i].pRegex.ed_re->refs = 0; ! else xtable[i].pRegex.pc_re->refs = 0; ! } } /* clear_rxcache_refs() */ /*--------------------------------------------------------------------*/ *************** *** 293,299 **** if (NULL != xtable[i].pString) { count_ref_from_string((char *)xtable[i].pString); ! count_rxcache_ref(xtable[i].pRegexp); } } /* for (i) */ --- 458,475 ---- if (NULL != xtable[i].pString) { count_ref_from_string((char *)xtable[i].pString); ! if (xtable[i].pc) ! { ! note_malloced_block_ref((char *)xtable[i].pRegex.pc_re); ! note_malloced_block_ref((char *)xtable[i].pRegex.pc_re->pProg); ! if (xtable[i].pRegex.pc_re->pHints) ! note_malloced_block_ref((char *)xtable[i].pRegex.pc_re->pHints); ! xtable[i].pRegex.pc_re->refs++; ! } else ! { ! note_malloced_block_ref((char *)xtable[i].pRegex.ed_re); ! xtable[i].pRegex.ed_re->refs++; ! } } } /* for (i) */ *************** *** 305,311 **** /* Mark all memory associated with one regexp structure and count * the refs. ! * This function is called both from rxcache as well as from ed. */ { --- 481,487 ---- /* Mark all memory associated with one regexp structure and count * the refs. ! * This function is called from ed. */ { *************** *** 313,321 **** pRegexp->refs++; } /* count_rxcache_ref() */ #endif /* if GC_SUPPORT */ ! #endif /* if RXCACHE_TABLE */ /*====================================================================*/ --- 489,555 ---- pRegexp->refs++; } /* count_rxcache_ref() */ + /*--------------------------------------------------------------------*/ #endif /* if GC_SUPPORT */ ! #else /* RXCACHE_TABLE undef'd*/ ! ! /*--------------------------------------------------------------------*/ ! /* Wrapper to use PCRE without cache */ ! ! #ifdef USE_PCRE ! pcregex* pcregex_cache(const char* expr, int opt) ! { ! pcregex *pRegexp; ! const char* pErrmsg; ! int erridx; ! ! xallocate(pRegexp, sizeof(pcregex), "rxcache entry"); ! ! pcre_malloc_size = 0; ! pcre_malloc_err = "compiling regex"; ! pRegexp->pProg = pcre_compile(expr, opt, &pErrmsg, &erridx, NULL); ! ! if (NULL == pRegexp->pProg) { ! xfree(pRegexp); ! error("pcre: %s at offset %d\n", pErrmsg, erridx); ! /* NOTREACHED */ ! return NULL; ! } ! ! pcre_malloc_err = "studying regex"; ! pRegexp->pHints = pcre_study(pRegexp->pProg, 0, &pErrmsg); ! ! if (pErrmsg) { ! xfree(pRegexp); ! error("pcre: %s\n", pErrmsg); ! /* NOTREACHED */ ! return NULL; ! } ! ! pRegexp->regalloc = pcre_malloc_size + sizeof(pRegexp); ! pRegexp->refs = 1; ! ! expr = (unsigned char *)make_shared_string((char *)expr); ! ! return pRegexp; ! } ! ! /*--------------------------------------------------------------------*/ ! ! void ! pcregex_free (pcregex * expr) ! ! /* Deallocate structure and all associated data ! */ ! ! { ! if (expr->pHints) xfree(expr->pHints); ! xfree(expr->pProg); ! xfree(expr); ! } /*====================================================================*/ + #endif /* USE_PCRE */ + #endif /* RXCACHE_TABLE undef'd */ diff -c /mud/src/driver/3-2-dev/src/rxcache.h ./rxcache.h *** /mud/src/driver/3-2-dev/src/rxcache.h Fri Feb 23 06:33:35 2001 --- ./rxcache.h Thu Jul 26 20:42:43 2001 *************** *** 5,10 **** --- 5,50 ---- #include "regexp.h" #include "strfuns.h" + #include "pcre-3.4/pcre.h" + + /* --- struct pcregex: regular expression basis --- + * + * This structure is used to hold a compiled regular expression as well + * as caching and refcounting data analog struct regexp + */ + + typedef struct pcregex { + pcre* pProg; /* The generated regular expression */ + pcre_extra* pHints; /* Study data */ + long regalloc; /* Allocated total length, used by rxcache */ + p_uint refs; /* Number of refs, used+maintained by rxcache */ + } pcregex; + + /* --- struct RxHashEntry: One expression hashtable entry --- + * + * The rxcache table consists of this entries + */ + + typedef struct RxHashEntry { + unsigned char * pString; /* Generator string, a shared string + * NULL if unused */ + p_uint hString; /* Hash of pString */ + Bool from_ed; /* The from_ed value */ + Bool excompat; /* The excompat value */ + Bool pc; /* Perl style regex? */ + union { + regexp* ed_re; /* ed style regex program */ + pcregex* pc_re; /* perl style regex program */ + } pRegex; + } RxHashEntry; + + #ifdef USE_PCRE + #define OVEC_LEN 300 /* is maximum, means 99 parentheses */ + extern void pcregex_free (pcregex *); + extern pcregex * pcregex_dup (pcregex *); + pcregex* pcregex_cache(const char* expr, int opt); + const char* pcre_error_message(int); + #endif USE_PCRE #ifdef RXCACHE_TABLE --311121419-828061240-996183711=:22388--