regexpr.pas 202 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978
  1. unit regexpr;
  2. {
  3. TRegExpr class library
  4. Delphi Regular Expressions
  5. Copyright (c) 1999-2004 Andrey V. Sorokin, St.Petersburg, Russia
  6. You can choose to use this Pascal unit in one of the two following licenses:
  7. Option 1>
  8. You may use this software in any kind of development,
  9. including comercial, redistribute, and modify it freely,
  10. under the following restrictions :
  11. 1. This software is provided as it is, without any kind of
  12. warranty given. Use it at Your own risk.The author is not
  13. responsible for any consequences of use of this software.
  14. 2. The origin of this software may not be mispresented, You
  15. must not claim that You wrote the original software. If
  16. You use this software in any kind of product, it would be
  17. appreciated that there in a information box, or in the
  18. documentation would be an acknowledgement like
  19. Partial Copyright (c) 2004 Andrey V. Sorokin
  20. https://sorokin.engineer/
  21. [email protected]
  22. 3. You may not have any income from distributing this source
  23. (or altered version of it) to other developers. When You
  24. use this product in a comercial package, the source may
  25. not be charged seperatly.
  26. 4. Altered versions must be plainly marked as such, and must
  27. not be misrepresented as being the original software.
  28. 5. RegExp Studio application and all the visual components as
  29. well as documentation is not part of the TRegExpr library
  30. and is not free for usage.
  31. https://sorokin.engineer/
  32. [email protected]
  33. Option 2>
  34. The same modified LGPL with static linking exception as the Free Pascal RTL
  35. }
  36. {
  37. program is essentially a linear encoding
  38. of a nondeterministic finite-state machine (aka syntax charts or
  39. "railroad normal form" in parsing technology). Each node is an opcode
  40. plus a "next" pointer, possibly plus an operand. "Next" pointers of
  41. all nodes except BRANCH implement concatenation; a "next" pointer with
  42. a BRANCH on both ends of it connects two alternatives. (Here we
  43. have one of the subtle syntax dependencies: an individual BRANCH (as
  44. opposed to a collection of them) is never concatenated with anything
  45. because of operator precedence.) The operand of some types of node is
  46. a literal string; for others, it is a node leading into a sub-FSM. In
  47. particular, the operand of a BRANCH node is the first node of the branch.
  48. (NB this is *not* a tree structure: the tail of the branch connects
  49. to the thing following the set of BRANCHes.)
  50. }
  51. interface
  52. { off $DEFINE DebugSynRegExpr }
  53. // ======== Determine compiler
  54. // ======== Define base compiler options
  55. {$BOOLEVAL OFF}
  56. {$EXTENDEDSYNTAX ON}
  57. {$LONGSTRINGS ON}
  58. {$MODE DELPHI} // Delphi-compatible mode in FreePascal
  59. {$INLINE ON}
  60. {$DEFINE COMPAT}
  61. // ======== Define options for TRegExpr engine
  62. { off $DEFINE UnicodeRE} // Use WideChar for characters and UnicodeString/WideString for strings
  63. { off $DEFINE UnicodeEx} // Support Unicode >0xFFFF, e.g. emoji, e.g. "." must find 2 WideChars of 1 emoji
  64. {$DEFINE UseWordChars} // Use WordChars property, otherwise fixed list 'a'..'z','A'..'Z','0'..'9','_'
  65. {$DEFINE UseSpaceChars} // Use SpaceChars property, otherwise fixed list
  66. {$DEFINE UseLineSep} // Use LineSeparators property, otherwise fixed line-break chars
  67. { off $DEFINE FastUnicodeData} // Use arrays for UpperCase/LowerCase/IsWordChar, they take 320K more memory
  68. {$DEFINE UseFirstCharSet} // Enable optimization, which finds possible first chars of input string
  69. {$DEFINE RegExpPCodeDump} // Enable method Dump() to show opcode as string
  70. {$IFNDEF FPC} // Not supported in FreePascal
  71. {$DEFINE reRealExceptionAddr} // Exceptions will point to appropriate source line, not to Error procedure
  72. {$ENDIF}
  73. {$DEFINE ComplexBraces} // Support braces in complex cases
  74. {$IFNDEF UnicodeRE}
  75. {$UNDEF UnicodeEx}
  76. {$UNDEF FastUnicodeData}
  77. {$ENDIF}
  78. {.$DEFINE Compat} // Enable compatability methods/properties for forked version in Free Pascal 3.0
  79. // ======== Define Pascal-language options
  80. // Define 'UseAsserts' option (do not edit this definitions).
  81. // Asserts used to catch 'strange bugs' in TRegExpr implementation (when something goes
  82. // completely wrong). You can swith asserts on/off with help of {$C+}/{$C-} compiler options.
  83. {$IFDEF D3} {$DEFINE UseAsserts} {$ENDIF}
  84. {$IFDEF FPC} {$DEFINE UseAsserts} {$ENDIF}
  85. // Define 'use subroutine parameters default values' option (do not edit this definition).
  86. {$IFDEF D4} {$DEFINE DefParam} {$ENDIF}
  87. {$IFDEF FPC} {$DEFINE DefParam} {$ENDIF}
  88. // Define 'OverMeth' options, to use method overloading (do not edit this definitions).
  89. {$IFDEF D5} {$DEFINE OverMeth} {$ENDIF}
  90. {$IFDEF FPC} {$DEFINE OverMeth} {$ENDIF}
  91. // Define 'InlineFuncs' options, to use inline keyword (do not edit this definitions).
  92. {$IFDEF D8} {$DEFINE InlineFuncs} {$ENDIF}
  93. {$IFDEF FPC} {$DEFINE InlineFuncs} {$ENDIF}
  94. uses
  95. SysUtils, // Exception
  96. {$IFDEF D2009}
  97. {$IFDEF D_XE2}
  98. System.Character,
  99. {$ELSE}
  100. Character,
  101. {$ENDIF}
  102. {$ENDIF}
  103. Classes; // TStrings in Split method
  104. type
  105. {$IFNDEF FPC}
  106. // Delphi doesn't have PtrInt but has NativeInt
  107. // but unfortunately NativeInt is declared wrongly in several versions
  108. {$IF SizeOf(Pointer)=4}
  109. PtrInt = Integer;
  110. PtrUInt = Cardinal;
  111. {$ELSE}
  112. PtrInt = Int64;
  113. PtrUInt = UInt64;
  114. {$IFEND}
  115. {$ENDIF}
  116. {$IFDEF UnicodeRE}
  117. PRegExprChar = PWideChar;
  118. {$IFDEF FPC}
  119. RegExprString = UnicodeString;
  120. {$ELSE}
  121. {$IFDEF D2009}
  122. RegExprString = UnicodeString;
  123. {$ELSE}
  124. RegExprString = WideString;
  125. {$ENDIF}
  126. {$ENDIF}
  127. REChar = WideChar;
  128. {$ELSE}
  129. PRegExprChar = PAnsiChar;
  130. RegExprString = AnsiString;
  131. REChar = AnsiChar;
  132. {$ENDIF}
  133. TREOp = REChar; // internal opcode type
  134. PREOp = ^TREOp;
  135. type
  136. TRegExprCharset = set of byte;
  137. const
  138. // Escape AnsiChar ('\' in common r.e.) used for escaping metachars (\w, \d etc)
  139. EscChar = '\';
  140. // Substitute method: prefix of group reference: $1 .. $9 and $<name>
  141. SubstituteGroupChar = '$';
  142. RegExprModifierI: boolean = False; // default value for ModifierI
  143. RegExprModifierR: boolean = True; // default value for ModifierR
  144. RegExprModifierS: boolean = True; // default value for ModifierS
  145. RegExprModifierG: boolean = True; // default value for ModifierG
  146. RegExprModifierM: boolean = False; // default value for ModifierM
  147. RegExprModifierX: boolean = False; // default value for ModifierX
  148. {$IFDEF UseSpaceChars}
  149. // default value for SpaceChars
  150. RegExprSpaceChars: RegExprString = ' '#$9#$A#$D#$C;
  151. {$ENDIF}
  152. {$IFDEF UseWordChars}
  153. // default value for WordChars
  154. RegExprWordChars: RegExprString = '0123456789'
  155. + 'abcdefghijklmnopqrstuvwxyz'
  156. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';
  157. {$ENDIF}
  158. {$IFDEF UseLineSep}
  159. // default value for LineSeparators
  160. RegExprLineSeparators: RegExprString = #$d#$a#$b#$c
  161. {$IFDEF UnicodeRE}
  162. + #$2028#$2029#$85
  163. {$ENDIF};
  164. {$ENDIF}
  165. // Tab and Unicode category "Space Separator":
  166. // https://www.compart.com/en/unicode/category/Zs
  167. RegExprHorzSeparators: RegExprString = #9#$20#$A0
  168. {$IFDEF UnicodeRE}
  169. + #$1680#$2000#$2001#$2002#$2003#$2004#$2005#$2006#$2007#$2008#$2009#$200A#$202F#$205F#$3000
  170. {$ENDIF};
  171. RegExprUsePairedBreak: boolean = True;
  172. RegExprReplaceLineBreak: RegExprString = sLineBreak;
  173. RegExprLookaheadIsAtomic: boolean = False;
  174. RegExprLookbehindIsAtomic: boolean = True;
  175. const
  176. // Max number of groups.
  177. // Be carefull - don't use values which overflow OP_CLOSE* opcode
  178. // (in this case you'll get compiler error).
  179. // Big value causes slower work and more stack required.
  180. RegexMaxGroups = 90;
  181. // Max possible value for RegexMaxGroups.
  182. // Don't change it! It's defined by internal TRegExpr design.
  183. RegexMaxMaxGroups = 255;
  184. // Max depth of recursion for (?R) and (?1)..(?9)
  185. RegexMaxRecursion = 20;
  186. {$IFDEF ComplexBraces}
  187. const
  188. LoopStackMax = 10; // max depth of loops stack //###0.925
  189. type
  190. TRegExprLoopStack = array [1 .. LoopStackMax] of integer;
  191. {$ENDIF}
  192. type
  193. TRegExprModifiers = record
  194. I: boolean;
  195. // Case-insensitive.
  196. R: boolean;
  197. // Extended syntax for Russian ranges in [].
  198. // If True, then а-я additionally includes letter 'ё',
  199. // А-Я additionally includes 'Ё', and а-Я includes all Russian letters.
  200. // Turn it off if it interferes with your national alphabet.
  201. S: boolean;
  202. // Dot '.' matches any AnsiChar, otherwise only [^\n].
  203. G: boolean;
  204. // Greedy. Switching it off switches all operators to non-greedy style,
  205. // so if G=False, then '*' works like '*?', '+' works like '+?' and so on.
  206. M: boolean;
  207. // Treat string as multiple lines. It changes `^' and `$' from
  208. // matching at only the very start/end of the string to the start/end
  209. // of any line anywhere within the string.
  210. X: boolean;
  211. // Allow comments in regex using # AnsiChar.
  212. end;
  213. function IsModifiersEqual(const A, B: TRegExprModifiers): boolean;
  214. type
  215. TRegExpr = class;
  216. TRegExprReplaceFunction = function(ARegExpr: TRegExpr): RegExprString of object;
  217. TRegExprCharChecker = function(ch: REChar): boolean of object;
  218. TRegExprCharCheckerArray = array[0 .. 30] of TRegExprCharChecker;
  219. TRegExprCharCheckerInfo = record
  220. CharBegin, CharEnd: REChar;
  221. CheckerIndex: integer;
  222. end;
  223. TRegExprCharCheckerInfos = array of TRegExprCharCheckerInfo;
  224. {$IFDEF Compat}
  225. TRegExprInvertCaseFunction = function(const Ch: REChar): REChar of object;
  226. {$ENDIF}
  227. TRegExprBounds = record
  228. GrpStart: array [0 .. RegexMaxGroups - 1] of PRegExprChar; // pointer to group start in InputString
  229. GrpEnd: array [0 .. RegexMaxGroups - 1] of PRegExprChar; // pointer to group end in InputString
  230. end;
  231. TRegExprBoundsArray = array[0 .. RegexMaxRecursion] of TRegExprBounds;
  232. { TRegExpr }
  233. TRegExpr = class
  234. private
  235. GrpBounds: TRegExprBoundsArray;
  236. GrpIndexes: array [0 .. RegexMaxGroups - 1] of integer; // map global group index to _capturing_ group index
  237. GrpNames: array [0 .. RegexMaxGroups - 1] of RegExprString; // names of groups, if non-empty
  238. GrpAtomic: array [0 .. RegexMaxGroups - 1] of boolean; // group[i] is atomic (filled in Compile)
  239. GrpAtomicDone: array [0 .. RegexMaxGroups - 1] of boolean; // atomic group[i] is "done" (used in Exec* only)
  240. GrpOpCodes: array [0 .. RegexMaxGroups - 1] of PRegExprChar; // pointer to opcode of group[i] (used by OP_SUBCALL*)
  241. GrpSubCalled: array [0 .. RegexMaxGroups - 1] of boolean; // group[i] is called by OP_SUBCALL*
  242. GrpCount: integer;
  243. {$IFDEF ComplexBraces}
  244. LoopStack: TRegExprLoopStack; // state before entering loop
  245. LoopStackIdx: integer; // 0 - out of all loops
  246. {$ENDIF}
  247. // The "internal use only" fields to pass info from compile
  248. // to execute that permits the execute phase to run lots faster on
  249. // simple cases.
  250. regAnchored: REChar; // is the match anchored (at beginning-of-line only)?
  251. // regAnchored permits very fast decisions on suitable starting points
  252. // for a match, cutting down the work a lot. regMust permits fast rejection
  253. // of lines that cannot possibly match. The regMust tests are costly enough
  254. // that regcomp() supplies a regMust only if the r.e. contains something
  255. // potentially expensive (at present, the only such thing detected is * or +
  256. // at the start of the r.e., which can involve a lot of backup). regMustLen is
  257. // supplied because the test in regexec() needs it and regcomp() is computing
  258. // it anyway.
  259. regMust: PRegExprChar; // string (pointer into program) that match must include, or nil
  260. regMustLen: integer; // length of regMust string
  261. regMustString: RegExprString; // string which must occur in match (got from regMust/regMustLen)
  262. regLookahead: boolean; // regex has _some_ lookahead
  263. regLookaheadNeg: boolean; // regex has _nagative_ lookahead
  264. regLookaheadGroup: integer; // index of group for lookahead
  265. regLookbehind: boolean; // regex has positive lookbehind
  266. regNestedCalls: integer; // some attempt to prevent 'catastrophic backtracking' but not used
  267. {$IFDEF UseFirstCharSet}
  268. FirstCharSet: TRegExprCharset;
  269. FirstCharArray: array[byte] of boolean;
  270. {$ENDIF}
  271. // work variables for Exec routines - save stack in recursion
  272. regInput: PRegExprChar; // pointer to currently handling AnsiChar of input string
  273. fInputStart: PRegExprChar; // pointer to first AnsiChar of input string
  274. fInputEnd: PRegExprChar; // pointer after last AnsiChar of input string
  275. fRegexStart: PRegExprChar; // pointer to first AnsiChar of regex
  276. fRegexEnd: PRegExprChar; // pointer after last AnsiChar of regex
  277. regCurrentGrp: integer; // index of group handling by OP_OPEN* opcode
  278. regRecursion: integer; // current level of recursion (?R) (?1); always 0 if no recursion is used
  279. // work variables for compiler's routines
  280. regParse: PRegExprChar; // pointer to currently handling AnsiChar of regex
  281. regNumBrackets: integer; // count of () brackets
  282. regDummy: REChar; // dummy pointer, used to detect 1st/2nd pass of Compile
  283. // if p=@regDummy, it is pass-1: opcode memory is not yet allocated
  284. programm: PRegExprChar; // pointer to opcode, =nil in pass-1
  285. regCode: PRegExprChar; // pointer to last emitted opcode; changing in pass-2, but =@regDummy in pass-1
  286. regCodeSize: integer; // total opcode size in REChars
  287. regCodeWork: PRegExprChar; // pointer to opcode, to first code after MAGIC
  288. regExactlyLen: PLongInt; // pointer to length of substring of OP_EXACTLY* inside opcode
  289. fSecondPass: boolean; // true inside pass-2 of Compile
  290. fExpression: RegExprString; // regex string
  291. fInputString: RegExprString; // input string
  292. fLastError: integer; // Error call sets code of LastError
  293. fLastErrorOpcode: TREOp;
  294. fLastErrorSymbol: REChar;
  295. fModifiers: TRegExprModifiers; // regex modifiers
  296. fCompModifiers: TRegExprModifiers; // compiler's copy of modifiers
  297. fProgModifiers: TRegExprModifiers; // modifiers values from last programm compilation
  298. {$IFDEF UseSpaceChars}
  299. fSpaceChars: RegExprString;
  300. {$ENDIF}
  301. {$IFDEF UseWordChars}
  302. fWordChars: RegExprString;
  303. {$ENDIF}
  304. {$IFDEF UseLineSep}
  305. fLineSeparators: RegExprString;
  306. {$ENDIF}
  307. fUsePairedBreak: boolean;
  308. fReplaceLineEnd: RegExprString; // string to use for "\n" in Substitute method
  309. fSlowChecksSizeMax: integer;
  310. // Exec() param ASlowChecks is set to True, when Length(InputString)<SlowChecksSizeMax
  311. // This ASlowChecks enables to use regMustString optimization
  312. {$IFDEF UseLineSep}
  313. {$IFNDEF UnicodeRE}
  314. fLineSepArray: array[byte] of boolean;
  315. {$ENDIF}
  316. {$ENDIF}
  317. CharCheckers: TRegExprCharCheckerArray;
  318. CharCheckerInfos: TRegExprCharCheckerInfos;
  319. CheckerIndex_Word: byte;
  320. CheckerIndex_NotWord: byte;
  321. CheckerIndex_Digit: byte;
  322. CheckerIndex_NotDigit: byte;
  323. CheckerIndex_Space: byte;
  324. CheckerIndex_NotSpace: byte;
  325. CheckerIndex_HorzSep: byte;
  326. CheckerIndex_NotHorzSep: byte;
  327. CheckerIndex_VertSep: byte;
  328. CheckerIndex_NotVertSep: byte;
  329. CheckerIndex_LowerAZ: byte;
  330. CheckerIndex_UpperAZ: byte;
  331. fHelper: TRegExpr;
  332. fHelperLen: integer;
  333. {$IFDEF Compat}
  334. fUseUnicodeWordDetection: boolean;
  335. fInvertCase: TRegExprInvertCaseFunction;
  336. fEmptyInputRaisesError: boolean;
  337. fUseOsLineEndOnReplace: boolean;
  338. function OldInvertCase(const Ch: REChar): REChar;
  339. function GetLinePairedSeparator: RegExprString;
  340. procedure SetLinePairedSeparator(const AValue: RegExprString);
  341. procedure SetUseOsLineEndOnReplace(AValue: boolean);
  342. {$ENDIF}
  343. procedure InitCharCheckers;
  344. function CharChecker_Word(ch: REChar): boolean;
  345. function CharChecker_NotWord(ch: REChar): boolean;
  346. function CharChecker_Space(ch: REChar): boolean;
  347. function CharChecker_NotSpace(ch: REChar): boolean;
  348. function CharChecker_Digit(ch: REChar): boolean;
  349. function CharChecker_NotDigit(ch: REChar): boolean;
  350. function CharChecker_HorzSep(ch: REChar): boolean;
  351. function CharChecker_NotHorzSep(ch: REChar): boolean;
  352. function CharChecker_VertSep(ch: REChar): boolean;
  353. function CharChecker_NotVertSep(ch: REChar): boolean;
  354. function CharChecker_LowerAZ(ch: REChar): boolean;
  355. function CharChecker_UpperAZ(ch: REChar): boolean;
  356. function DumpCheckerIndex(N: byte): RegExprString;
  357. function DumpCategoryChars(ch, ch2: REChar; Positive: boolean): RegExprString;
  358. procedure ClearMatches; {$IFDEF InlineFuncs}inline;{$ENDIF}
  359. procedure ClearInternalIndexes; {$IFDEF InlineFuncs}inline;{$ENDIF}
  360. function FindInCharClass(ABuffer: PRegExprChar; AChar: REChar; AIgnoreCase: boolean): boolean;
  361. procedure GetCharSetFromCharClass(ABuffer: PRegExprChar; AIgnoreCase: boolean; var ARes: TRegExprCharset);
  362. procedure GetCharSetFromSpaceChars(var ARes: TRegExprCharset); {$IFDEF InlineFuncs}inline;{$ENDIF}
  363. procedure GetCharSetFromWordChars(var ARes: TRegExprCharSet); {$IFDEF InlineFuncs}inline;{$ENDIF}
  364. function IsWordChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  365. function IsSpaceChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  366. function IsCustomLineSeparator(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  367. {$IFDEF UseLineSep}
  368. procedure InitLineSepArray;
  369. {$ENDIF}
  370. procedure FindGroupName(APtr, AEndPtr: PRegExprChar; AEndChar: REChar; var AName: RegExprString);
  371. // Mark programm as having to be [re]compiled
  372. procedure InvalidateProgramm;
  373. // Check if we can use compiled regex, compile it if something changed
  374. function IsProgrammOk: boolean;
  375. procedure SetExpression(const AStr: RegExprString);
  376. function GetModifierStr: RegExprString;
  377. procedure SetModifierStr(const AStr: RegExprString);
  378. function GetModifierG: boolean;
  379. function GetModifierI: boolean;
  380. function GetModifierM: boolean;
  381. function GetModifierR: boolean;
  382. function GetModifierS: boolean;
  383. function GetModifierX: boolean;
  384. procedure SetModifierG(AValue: boolean);
  385. procedure SetModifierI(AValue: boolean);
  386. procedure SetModifierM(AValue: boolean);
  387. procedure SetModifierR(AValue: boolean);
  388. procedure SetModifierS(AValue: boolean);
  389. procedure SetModifierX(AValue: boolean);
  390. // Default handler raises exception ERegExpr with
  391. // Message = ErrorMsg (AErrorID), ErrorCode = AErrorID
  392. // and CompilerErrorPos = value of property CompilerErrorPos.
  393. procedure Error(AErrorID: integer); virtual; // error handler.
  394. { ==================== Compiler section =================== }
  395. // compile a regular expression into internal code
  396. function CompileRegExpr(ARegExp: PRegExprChar): boolean;
  397. // set the next-pointer at the end of a node chain
  398. procedure Tail(p: PRegExprChar; val: PRegExprChar);
  399. // regoptail - regtail on operand of first argument; nop if operandless
  400. procedure OpTail(p: PRegExprChar; val: PRegExprChar);
  401. // regnode - emit a node, return location
  402. function EmitNode(op: TREOp): PRegExprChar;
  403. // emit (if appropriate) a byte of code
  404. procedure EmitC(ch: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  405. // emit LongInt value
  406. procedure EmitInt(AValue: LongInt); {$IFDEF InlineFuncs}inline;{$ENDIF}
  407. // emit back-reference to group
  408. function EmitGroupRef(AIndex: integer; AIgnoreCase: boolean): PRegExprChar;
  409. {$IFDEF FastUnicodeData}
  410. procedure FindCategoryName(var scan: PRegExprChar; var ch1, ch2: REChar);
  411. function EmitCategoryMain(APositive: boolean): PRegExprChar;
  412. {$ENDIF}
  413. // insert an operator in front of already-emitted operand
  414. // Means relocating the operand.
  415. procedure InsertOperator(op: TREOp; opnd: PRegExprChar; sz: integer);
  416. // ###0.90
  417. // regular expression, i.e. main body or parenthesized thing
  418. function ParseReg(InBrackets: boolean; var FlagParse: integer): PRegExprChar;
  419. // one alternative of an | operator
  420. function ParseBranch(var FlagParse: integer): PRegExprChar;
  421. // something followed by possible [*+?]
  422. function ParsePiece(var FlagParse: integer): PRegExprChar;
  423. function HexDig(Ch: REChar): integer;
  424. function UnQuoteChar(var APtr, AEnd: PRegExprChar): REChar;
  425. // the lowest level
  426. function ParseAtom(var FlagParse: integer): PRegExprChar;
  427. // current pos in r.e. - for error hanling
  428. function GetCompilerErrorPos: PtrInt;
  429. {$IFDEF UseFirstCharSet} // ###0.929
  430. procedure FillFirstCharSet(prog: PRegExprChar);
  431. {$ENDIF}
  432. { ===================== Matching section =================== }
  433. // repeatedly match something simple, report how many
  434. function FindRepeated(p: PRegExprChar; AMax: integer): integer;
  435. // dig the "next" pointer out of a node
  436. function regNext(p: PRegExprChar): PRegExprChar;
  437. // recursively matching routine
  438. function MatchPrim(prog: PRegExprChar): boolean;
  439. // match at specific position only, called from ExecPrim
  440. function MatchAtOnePos(APos: PRegExprChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  441. // Exec for stored InputString
  442. function ExecPrim(AOffset: integer; ATryOnce, ASlowChecks, ABackward: boolean): boolean;
  443. function GetSubExprCount: integer;
  444. function GetMatchPos(Idx: integer): PtrInt;
  445. function GetMatchLen(Idx: integer): PtrInt;
  446. function GetMatch(Idx: integer): RegExprString;
  447. procedure SetInputString(const AInputString: RegExprString);
  448. procedure SetInputRange(AStart, AEnd: PRegExprChar);
  449. {$IFDEF UseLineSep}
  450. procedure SetLineSeparators(const AStr: RegExprString);
  451. {$ENDIF}
  452. procedure SetUsePairedBreak(AValue: boolean);
  453. public
  454. constructor Create; {$IFDEF OverMeth} overload;
  455. constructor Create(const AExpression: RegExprString); overload;
  456. {$ENDIF}
  457. destructor Destroy; override;
  458. class function VersionMajor: integer;
  459. class function VersionMinor: integer;
  460. // match a programm against a string AInputString
  461. // !!! Exec store AInputString into InputString property
  462. // For Delphi 5 and higher available overloaded versions - first without
  463. // parameter (uses already assigned to InputString property value)
  464. // and second that has int parameter and is same as ExecPos
  465. function Exec(const AInputString: RegExprString): boolean;
  466. {$IFDEF OverMeth} overload;
  467. function Exec: boolean; overload;
  468. function Exec(AOffset: integer): boolean; overload;
  469. {$ENDIF}
  470. // find next match:
  471. // ExecNext;
  472. // works the same as
  473. // if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)
  474. // else ExecPos (MatchPos [0] + MatchLen [0]);
  475. // but it's more simpler !
  476. // Raises exception if used without preceeding SUCCESSFUL call to
  477. // Exec* (Exec, ExecPos, ExecNext). So You always must use something like
  478. // if Exec (InputString) then repeat { proceed results} until not ExecNext;
  479. function ExecNext(ABackward: boolean {$IFDEF DefParam} = False{$ENDIF}): boolean;
  480. // find match for InputString starting from AOffset position
  481. // (AOffset=1 - first AnsiChar of InputString)
  482. function ExecPos(AOffset: integer {$IFDEF DefParam} = 1{$ENDIF}): boolean;
  483. {$IFDEF OverMeth} overload;
  484. function ExecPos(AOffset: integer; ATryOnce, ABackward: boolean): boolean; overload;
  485. {$ENDIF}
  486. // Returns ATemplate with '$&' or '$0' replaced by whole r.e.
  487. // occurence and '$1'...'$nn' replaced by subexpression with given index.
  488. // Symbol '$' is used instead of '\' (for future extensions
  489. // and for more Perl-compatibility) and accepts more than one digit.
  490. // If you want to place into template raw '$' or '\', use prefix '\'.
  491. // Example: '1\$ is $2\\rub\\' -> '1$ is <Match[2]>\rub\'
  492. // If you want to place any number after '$' you must enclose it
  493. // with curly braces: '${12}'.
  494. // Example: 'a$12bc' -> 'a<Match[12]>bc'
  495. // 'a${1}2bc' -> 'a<Match[1]>2bc'.
  496. function Substitute(const ATemplate: RegExprString): RegExprString;
  497. // Splits AInputStr to list by positions of all r.e. occurencies.
  498. // Internally calls Exec, ExecNext.
  499. procedure Split(const AInputStr: RegExprString; APieces: TStrings);
  500. function Replace(const AInputStr: RegExprString;
  501. const AReplaceStr: RegExprString;
  502. AUseSubstitution: boolean{$IFDEF DefParam} = False{$ENDIF}) // ###0.946
  503. : RegExprString; {$IFDEF OverMeth} overload;
  504. function Replace(const AInputStr: RegExprString;
  505. AReplaceFunc: TRegExprReplaceFunction): RegExprString; overload;
  506. {$ENDIF}
  507. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr.
  508. // If AUseSubstitution is true, then AReplaceStr will be used
  509. // as template for Substitution methods.
  510. // For example:
  511. // Expression := '({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*';
  512. // Replace ('BLOCK( test1)', 'def "$1" value "$2"', True);
  513. // will return: def 'BLOCK' value 'test1'
  514. // Replace ('BLOCK( test1)', 'def "$1" value "$2"')
  515. // will return: def "$1" value "$2"
  516. // Internally calls Exec, ExecNext.
  517. // Overloaded version and ReplaceEx operate with callback function,
  518. // so you can implement really complex functionality.
  519. function ReplaceEx(const AInputStr: RegExprString;
  520. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  521. {$IFDEF Compat}
  522. function ExecPos(AOffset: integer; ATryOnce: boolean): boolean; overload; deprecated 'Use modern form of ExecPos()';
  523. class function InvertCaseFunction(const Ch: REChar): REChar; deprecated 'This has no effect now';
  524. property InvertCase: TRegExprInvertCaseFunction read fInvertCase write fInvertCase; deprecated 'This has no effect now';
  525. property UseUnicodeWordDetection: boolean read fUseUnicodeWordDetection write fUseUnicodeWordDetection; deprecated 'This has no effect, use {$DEFINE UnicodeRE} instead';
  526. property LinePairedSeparator: RegExprString read GetLinePairedSeparator write SetLinePairedSeparator; deprecated 'This has no effect now';
  527. property EmptyInputRaisesError: boolean read fEmptyInputRaisesError write fEmptyInputRaisesError; deprecated 'This has no effect now';
  528. property UseOsLineEndOnReplace: boolean read fUseOsLineEndOnReplace write SetUseOsLineEndOnReplace; deprecated 'Use property ReplaceLineEnd instead';
  529. {$ENDIF}
  530. // Returns ID of last error, 0 if no errors (unusable if
  531. // Error method raises exception) and clear internal status
  532. // into 0 (no errors).
  533. function LastError: integer;
  534. // Returns Error message for error with ID = AErrorID.
  535. function ErrorMsg(AErrorID: integer): RegExprString; virtual;
  536. // Re-compile regex
  537. procedure Compile;
  538. {$IFDEF RegExpPCodeDump}
  539. // Show compiled regex in textual form
  540. function Dump: RegExprString;
  541. // Show single opcode in textual form
  542. function DumpOp(op: TREOp): RegExprString;
  543. {$ENDIF}
  544. function IsCompiled: boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  545. // Opcode contains only operations for fixed match length: EXACTLY*, ANY*, etc
  546. function IsFixedLength(var op: TREOp; var ALen: integer): boolean;
  547. // Regular expression.
  548. // For optimization, TRegExpr will automatically compiles it into 'P-code'
  549. // (You can see it with help of Dump method) and stores in internal
  550. // structures. Real [re]compilation occures only when it really needed -
  551. // while calling Exec, ExecNext, Substitute, Dump, etc
  552. // and only if Expression or other P-code affected properties was changed
  553. // after last [re]compilation.
  554. // If any errors while [re]compilation occures, Error method is called
  555. // (by default Error raises exception - see below)
  556. property Expression: RegExprString read fExpression write SetExpression;
  557. // Set/get default values of r.e.syntax modifiers. Modifiers in
  558. // r.e. (?ismx-ismx) will replace this default values.
  559. // If you try to set unsupported modifier, Error will be called
  560. // (by defaul Error raises exception ERegExpr).
  561. property ModifierStr: RegExprString read GetModifierStr write SetModifierStr;
  562. property ModifierI: boolean read GetModifierI write SetModifierI;
  563. property ModifierR: boolean read GetModifierR write SetModifierR;
  564. property ModifierS: boolean read GetModifierS write SetModifierS;
  565. property ModifierG: boolean read GetModifierG write SetModifierG;
  566. property ModifierM: boolean read GetModifierM write SetModifierM;
  567. property ModifierX: boolean read GetModifierX write SetModifierX;
  568. // returns current input string (from last Exec call or last assign
  569. // to this property).
  570. // Any assignment to this property clear Match* properties !
  571. property InputString: RegExprString read fInputString write SetInputString;
  572. // Number of subexpressions has been found in last Exec* call.
  573. // If there are no subexpr. but whole expr was found (Exec* returned True),
  574. // then SubExprMatchCount=0, if no subexpressions nor whole
  575. // r.e. found (Exec* returned false) then SubExprMatchCount=-1.
  576. // Note, that some subexpr. may be not found and for such
  577. // subexpr. MathPos=MatchLen=-1 and Match=''.
  578. // For example: Expression := '(1)?2(3)?';
  579. // Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'
  580. // Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'
  581. // Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'
  582. // Exec ('2'): SubExprMatchCount=0, Match[0]='2'
  583. // Exec ('7') - return False: SubExprMatchCount=-1
  584. property SubExprMatchCount: integer read GetSubExprCount;
  585. // pos of entrance subexpr. #Idx into tested in last Exec*
  586. // string. First subexpr. has Idx=1, last - MatchCount,
  587. // whole r.e. has Idx=0.
  588. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  589. // not found in input string.
  590. property MatchPos[Idx: integer]: PtrInt read GetMatchPos;
  591. // len of entrance subexpr. #Idx r.e. into tested in last Exec*
  592. // string. First subexpr. has Idx=1, last - MatchCount,
  593. // whole r.e. has Idx=0.
  594. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  595. // not found in input string.
  596. // Remember - MatchLen may be 0 (if r.e. match empty string) !
  597. property MatchLen[Idx: integer]: PtrInt read GetMatchLen;
  598. // == copy (InputString, MatchPos [Idx], MatchLen [Idx])
  599. // Returns '' if in r.e. no such subexpr. or this subexpr.
  600. // not found in input string.
  601. property Match[Idx: integer]: RegExprString read GetMatch;
  602. // get index of group (subexpression) by name, to support named groups
  603. // like in Python: (?P<name>regex)
  604. function MatchIndexFromName(const AName: RegExprString): integer;
  605. function MatchFromName(const AName: RegExprString): RegExprString;
  606. // Returns position in r.e. where compiler stopped.
  607. // Useful for error diagnostics
  608. property CompilerErrorPos: PtrInt read GetCompilerErrorPos;
  609. {$IFDEF UseSpaceChars}
  610. // Contains chars, treated as /s (initially filled with RegExprSpaceChars
  611. // global constant)
  612. property SpaceChars: RegExprString read fSpaceChars write fSpaceChars;
  613. // ###0.927
  614. {$ENDIF}
  615. {$IFDEF UseWordChars}
  616. // Contains chars, treated as /w (initially filled with RegExprWordChars
  617. // global constant)
  618. property WordChars: RegExprString read fWordChars write fWordChars;
  619. // ###0.929
  620. {$ENDIF}
  621. {$IFDEF UseLineSep}
  622. // line separators (like \n in Unix)
  623. property LineSeparators: RegExprString read fLineSeparators write SetLineSeparators; // ###0.941
  624. {$ENDIF}
  625. // support paired line-break CR LF
  626. property UseLinePairedBreak: boolean read fUsePairedBreak write SetUsePairedBreak;
  627. property ReplaceLineEnd: RegExprString read fReplaceLineEnd write fReplaceLineEnd;
  628. property SlowChecksSizeMax: integer read fSlowChecksSizeMax write fSlowChecksSizeMax;
  629. end;
  630. type
  631. ERegExpr = class(Exception)
  632. public
  633. ErrorCode: integer;
  634. CompilerErrorPos: PtrInt;
  635. end;
  636. // true if string AInputString match regular expression ARegExpr
  637. // ! will raise exeption if syntax errors in ARegExpr
  638. function ExecRegExpr(const ARegExpr, AInputStr: RegExprString): boolean;
  639. // Split AInputStr into APieces by r.e. ARegExpr occurencies
  640. procedure SplitRegExpr(const ARegExpr, AInputStr: RegExprString;
  641. APieces: TStrings);
  642. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr
  643. // If AUseSubstitution is true, then AReplaceStr will be used
  644. // as template for Substitution methods.
  645. // For example:
  646. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  647. // 'BLOCK( test1)', 'def "$1" value "$2"', True)
  648. // will return: def 'BLOCK' value 'test1'
  649. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  650. // 'BLOCK( test1)', 'def "$1" value "$2"')
  651. // will return: def "$1" value "$2"
  652. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  653. AUseSubstitution: boolean{$IFDEF DefParam} = False{$ENDIF}): RegExprString;
  654. {$IFDEF OverMeth}overload; // ###0.947
  655. // Alternate form allowing to set more parameters.
  656. type
  657. TRegexReplaceOption = (
  658. rroModifierI,
  659. rroModifierR,
  660. rroModifierS,
  661. rroModifierG,
  662. rroModifierM,
  663. rroModifierX,
  664. rroUseSubstitution,
  665. rroUseOsLineEnd
  666. );
  667. TRegexReplaceOptions = set of TRegexReplaceOption;
  668. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  669. Options: TRegexReplaceOptions): RegExprString; overload;
  670. {$ENDIF}
  671. // Replace all metachars with its safe representation,
  672. // for example 'abc$cd.(' converts into 'abc\$cd\.\('
  673. // This function useful for r.e. autogeneration from
  674. // user input
  675. function QuoteRegExprMetaChars(const AStr: RegExprString): RegExprString;
  676. // Makes list of subexpressions found in ARegExpr r.e.
  677. // In ASubExps every item represent subexpression,
  678. // from first to last, in format:
  679. // String - subexpression text (without '()')
  680. // low word of Object - starting position in ARegExpr, including '('
  681. // if exists! (first position is 1)
  682. // high word of Object - length, including starting '(' and ending ')'
  683. // if exist!
  684. // AExtendedSyntax - must be True if modifier /m will be On while
  685. // using the r.e.
  686. // Useful for GUI editors of r.e. etc (You can find example of using
  687. // in TestRExp.dpr project)
  688. // Returns
  689. // 0 Success. No unbalanced brackets was found;
  690. // -1 There are not enough closing brackets ')';
  691. // -(n+1) At position n was found opening '[' without //###0.942
  692. // corresponding closing ']';
  693. // n At position n was found closing bracket ')' without
  694. // corresponding opening '('.
  695. // If Result <> 0, then ASubExpr can contain empty items or illegal ones
  696. function RegExprSubExpressions(const ARegExpr: RegExprString; ASubExprs: TStrings;
  697. AExtendedSyntax: boolean{$IFDEF DefParam} = False{$ENDIF}): integer;
  698. implementation
  699. {$IFDEF FastUnicodeData}
  700. uses
  701. regexpr_unicodedata;
  702. {$ENDIF}
  703. const
  704. // TRegExpr.VersionMajor/Minor return values of these constants:
  705. REVersionMajor = 1;
  706. REVersionMinor = 158;
  707. OpKind_End = REChar(1);
  708. OpKind_MetaClass = REChar(2);
  709. OpKind_Range = REChar(3);
  710. OpKind_Char = REChar(4);
  711. OpKind_CategoryYes = REChar(5);
  712. OpKind_CategoryNo = REChar(6);
  713. RegExprAllSet = [0 .. 255];
  714. RegExprWordSet = [Ord('a') .. Ord('z'), Ord('A') .. Ord('Z'), Ord('0') .. Ord('9'), Ord('_')];
  715. RegExprDigitSet = [Ord('0') .. Ord('9')];
  716. RegExprLowerAzSet = [Ord('a') .. Ord('z')];
  717. RegExprUpperAzSet = [Ord('A') .. Ord('Z')];
  718. RegExprAllAzSet = RegExprLowerAzSet + RegExprUpperAzSet;
  719. RegExprSpaceSet = [Ord(' '), $9, $A, $D, $C];
  720. RegExprLineSeparatorsSet = [$d, $a, $b, $c] {$IFDEF UnicodeRE} + [$85] {$ENDIF};
  721. RegExprHorzSeparatorsSet = [9, $20, $A0];
  722. MaxBracesArg = $7FFFFFFF - 1; // max value for {n,m} arguments //###0.933
  723. type
  724. TRENextOff = PtrInt;
  725. // internal Next "pointer" (offset to current p-code) //###0.933
  726. PRENextOff = ^TRENextOff;
  727. // used for extracting Next "pointers" from compiled r.e. //###0.933
  728. TREBracesArg = integer; // type of {m,n} arguments
  729. PREBracesArg = ^TREBracesArg;
  730. TREGroupKind = (
  731. gkNormalGroup,
  732. gkNonCapturingGroup,
  733. gkNamedGroupReference,
  734. gkComment,
  735. gkModifierString,
  736. gkLookahead,
  737. gkLookaheadNeg,
  738. gkLookbehind,
  739. gkLookbehindNeg,
  740. gkRecursion,
  741. gkSubCall
  742. );
  743. // Alexey T.: handling of that define FPC_REQUIRES_PROPER_ALIGNMENT was present even 15 years ago,
  744. // but with it, we have failing of some RegEx tests, on ARM64 CPU.
  745. // If I undefine FPC_REQUIRES_PROPER_ALIGNMENT, all tests run OK on ARM64 again.
  746. {$undef FPC_REQUIRES_PROPER_ALIGNMENT}
  747. const
  748. REOpSz = SizeOf(TREOp) div SizeOf(REChar);
  749. // size of OP_ command in REChars
  750. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  751. // add space for aligning pointer
  752. // -1 is the correct max size but also needed for InsertOperator that needs a multiple of pointer size
  753. RENextOffSz = (2 * SizeOf(TRENextOff) div SizeOf(REChar)) - 1;
  754. REBracesArgSz = (2 * SizeOf(TREBracesArg) div SizeOf(REChar));
  755. // add space for aligning pointer
  756. {$ELSE}
  757. RENextOffSz = (SizeOf(TRENextOff) div SizeOf(REChar));
  758. // size of Next pointer in REChars
  759. REBracesArgSz = SizeOf(TREBracesArg) div SizeOf(REChar);
  760. // size of BRACES arguments in REChars
  761. {$ENDIF}
  762. RENumberSz = SizeOf(LongInt) div SizeOf(REChar);
  763. type
  764. PtrPair = {$IFDEF UnicodeRE} ^LongInt; {$ELSE} ^Word; {$ENDIF}
  765. function IsPairedBreak(p: PRegExprChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  766. const
  767. cBreak = {$IFDEF UnicodeRE} $000D000A; {$ELSE} $0D0A; {$ENDIF}
  768. begin
  769. Result := PtrPair(p)^ = cBreak;
  770. end;
  771. function _FindCharInBuffer(SBegin, SEnd: PRegExprChar; Ch: REChar): PRegExprChar; {$IFDEF InlineFuncs}inline;{$ENDIF}
  772. begin
  773. while SBegin < SEnd do
  774. begin
  775. if SBegin^ = Ch then
  776. begin
  777. Result := SBegin;
  778. Exit;
  779. end;
  780. Inc(SBegin);
  781. end;
  782. Result := nil;
  783. end;
  784. function IsIgnoredChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  785. begin
  786. case AChar of
  787. ' ', #9, #$d, #$a:
  788. Result := True
  789. else
  790. Result := False;
  791. end;
  792. end;
  793. function _IsMetaChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  794. begin
  795. case AChar of
  796. 'd', 'D',
  797. 's', 'S',
  798. 'w', 'W',
  799. 'v', 'V',
  800. 'h', 'H':
  801. Result := True
  802. else
  803. Result := False;
  804. end;
  805. end;
  806. function AlignToPtr(const p: Pointer): Pointer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  807. begin
  808. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  809. Result := Align(p, SizeOf(Pointer));
  810. {$ELSE}
  811. Result := p;
  812. {$ENDIF}
  813. end;
  814. function AlignToInt(const p: Pointer): Pointer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  815. begin
  816. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  817. Result := Align(p, SizeOf(integer));
  818. {$ELSE}
  819. Result := p;
  820. {$ENDIF}
  821. end;
  822. {$IFDEF FastUnicodeData}
  823. function _UpperCase(Ch: REChar): REChar; inline;
  824. begin
  825. Result := CharUpperArray[Ord(Ch)];
  826. end;
  827. function _LowerCase(Ch: REChar): REChar; inline;
  828. begin
  829. Result := CharLowerArray[Ord(Ch)];
  830. end;
  831. {$ELSE}
  832. function _UpperCase(Ch: REChar): REChar;
  833. begin
  834. Result := Ch;
  835. if (Ch >= 'a') and (Ch <= 'z') then
  836. begin
  837. Dec(Result, 32);
  838. Exit;
  839. end;
  840. if Ord(Ch) < 128 then
  841. Exit;
  842. {$IFDEF FPC}
  843. {$IFDEF UnicodeRE}
  844. Result := UnicodeUpperCase(Ch)[1];
  845. {$ELSE}
  846. Result := AnsiUpperCase(Ch)[1];
  847. {$ENDIF}
  848. {$ELSE}
  849. {$IFDEF UnicodeRE}
  850. {$IFDEF D_XE4}
  851. Result := Ch.ToUpper;
  852. {$ELSE}
  853. {$IFDEF D2009}
  854. Result := TCharacter.ToUpper(Ch);
  855. {$ENDIF}
  856. {$ENDIF}
  857. {$ELSE}
  858. Result := AnsiUpperCase(Ch)[1];
  859. {$ENDIF}
  860. {$ENDIF}
  861. end;
  862. function _LowerCase(Ch: REChar): REChar;
  863. begin
  864. Result := Ch;
  865. if (Ch >= 'A') and (Ch <= 'Z') then
  866. begin
  867. Inc(Result, 32);
  868. Exit;
  869. end;
  870. if Ord(Ch) < 128 then
  871. Exit;
  872. {$IFDEF FPC}
  873. {$IFDEF UnicodeRE}
  874. Result := UnicodeLowerCase(Ch)[1];
  875. {$ELSE}
  876. Result := AnsiLowerCase(Ch)[1];
  877. {$ENDIF}
  878. {$ELSE}
  879. {$IFDEF UnicodeRE}
  880. {$IFDEF D_XE4}
  881. Result := Ch.ToLower;
  882. {$ELSE}
  883. {$IFDEF D2009}
  884. Result := TCharacter.ToLower(Ch);
  885. {$ENDIF}
  886. {$ENDIF}
  887. {$ELSE}
  888. Result := AnsiLowerCase(Ch)[1];
  889. {$ENDIF}
  890. {$ENDIF}
  891. end;
  892. {$ENDIF}
  893. function InvertCase(const Ch: REChar): REChar; {$IFDEF InlineFuncs}inline;{$ENDIF}
  894. begin
  895. Result := _UpperCase(Ch);
  896. if Result = Ch then
  897. Result := _LowerCase(Ch);
  898. end;
  899. function _FindClosingBracket(P, PEnd: PRegExprChar): PRegExprChar;
  900. var
  901. Level: integer;
  902. begin
  903. Result := nil;
  904. Level := 1;
  905. repeat
  906. if P >= PEnd then Exit;
  907. case P^ of
  908. EscChar:
  909. Inc(P);
  910. '(':
  911. begin
  912. Inc(Level);
  913. end;
  914. ')':
  915. begin
  916. Dec(Level);
  917. if Level = 0 then
  918. begin
  919. Result := P;
  920. Exit;
  921. end;
  922. end;
  923. end;
  924. Inc(P);
  925. until False;
  926. end;
  927. {$IFDEF UNICODEEX}
  928. procedure IncUnicode(var p: PRegExprChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  929. // make additional increment if we are on low-surrogate AnsiChar
  930. // no need to check p<fInputEnd, at the end of string we have chr(0)
  931. var
  932. ch: REChar;
  933. begin
  934. Inc(p);
  935. ch := p^;
  936. if (Ord(ch) >= $DC00) and (Ord(ch) <= $DFFF) then
  937. Inc(p);
  938. end;
  939. procedure IncUnicode2(var p: PRegExprChar; var N: integer); {$IFDEF InlineFuncs}inline;{$ENDIF}
  940. var
  941. ch: REChar;
  942. begin
  943. Inc(p);
  944. Inc(N);
  945. ch := p^;
  946. if (Ord(ch) >= $DC00) and (Ord(ch) <= $DFFF) then
  947. begin
  948. Inc(p);
  949. Inc(N);
  950. end;
  951. end;
  952. {$ENDIF}
  953. { ============================================================= }
  954. { ===================== Global functions ====================== }
  955. { ============================================================= }
  956. function IsModifiersEqual(const A, B: TRegExprModifiers): boolean;
  957. begin
  958. Result :=
  959. (A.I = B.I) and
  960. (A.G = B.G) and
  961. (A.M = B.M) and
  962. (A.S = B.S) and
  963. (A.R = B.R) and
  964. (A.X = B.X);
  965. end;
  966. function ParseModifiers(const APtr: PRegExprChar;
  967. ALen: integer;
  968. var AValue: TRegExprModifiers): boolean;
  969. // Parse string and set AValue if it's in format 'ismxrg-ismxrg'
  970. var
  971. IsOn: boolean;
  972. i: integer;
  973. begin
  974. Result := True;
  975. IsOn := True;
  976. for i := 0 to ALen-1 do
  977. case APtr[i] of
  978. '-':
  979. IsOn := False;
  980. 'I', 'i':
  981. AValue.I := IsOn;
  982. 'R', 'r':
  983. AValue.R := IsOn;
  984. 'S', 's':
  985. AValue.S := IsOn;
  986. 'G', 'g':
  987. AValue.G := IsOn;
  988. 'M', 'm':
  989. AValue.M := IsOn;
  990. 'X', 'x':
  991. AValue.X := IsOn;
  992. else
  993. begin
  994. Result := False;
  995. Exit;
  996. end;
  997. end;
  998. end;
  999. function ExecRegExpr(const ARegExpr, AInputStr: RegExprString): boolean;
  1000. var
  1001. r: TRegExpr;
  1002. begin
  1003. r := TRegExpr.Create;
  1004. try
  1005. r.Expression := ARegExpr;
  1006. Result := r.Exec(AInputStr);
  1007. finally
  1008. r.Free;
  1009. end;
  1010. end; { of function ExecRegExpr
  1011. -------------------------------------------------------------- }
  1012. procedure SplitRegExpr(const ARegExpr, AInputStr: RegExprString;
  1013. APieces: TStrings);
  1014. var
  1015. r: TRegExpr;
  1016. begin
  1017. APieces.Clear;
  1018. r := TRegExpr.Create;
  1019. try
  1020. r.Expression := ARegExpr;
  1021. r.Split(AInputStr, APieces);
  1022. finally
  1023. r.Free;
  1024. end;
  1025. end; { of procedure SplitRegExpr
  1026. -------------------------------------------------------------- }
  1027. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  1028. AUseSubstitution: boolean{$IFDEF DefParam} = False{$ENDIF}): RegExprString;
  1029. begin
  1030. with TRegExpr.Create do
  1031. try
  1032. Expression := ARegExpr;
  1033. Result := Replace(AInputStr, AReplaceStr, AUseSubstitution);
  1034. finally
  1035. Free;
  1036. end;
  1037. end; { of function ReplaceRegExpr
  1038. -------------------------------------------------------------- }
  1039. {$IFDEF OverMeth}
  1040. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  1041. Options: TRegexReplaceOptions): RegExprString; overload;
  1042. begin
  1043. with TRegExpr.Create do
  1044. try
  1045. ModifierI := (rroModifierI in Options);
  1046. ModifierR := (rroModifierR in Options);
  1047. ModifierS := (rroModifierS in Options);
  1048. ModifierG := (rroModifierG in Options);
  1049. ModifierM := (rroModifierM in Options);
  1050. ModifierX := (rroModifierX in Options);
  1051. // Set this after the above, if the regex contains modifiers, they will be applied.
  1052. Expression := ARegExpr;
  1053. if rroUseOsLineEnd in Options then
  1054. ReplaceLineEnd := sLineBreak
  1055. else
  1056. ReplaceLineEnd := #10;
  1057. Result := Replace(AInputStr, AReplaceStr, rroUseSubstitution in Options);
  1058. finally
  1059. Free;
  1060. end;
  1061. end;
  1062. {$ENDIF}
  1063. (*
  1064. const
  1065. MetaChars_Init = '^$.[()|?+*' + EscChar + '{';
  1066. MetaChars = MetaChars_Init; // not needed to be a variable, const is faster
  1067. MetaAll = MetaChars_Init + ']}'; // Very similar to MetaChars, but slighly changed.
  1068. *)
  1069. function _IsMetaSymbol1(ch: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1070. begin
  1071. case ch of
  1072. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{':
  1073. Result := True
  1074. else
  1075. Result := False
  1076. end;
  1077. end;
  1078. function _IsMetaSymbol2(ch: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1079. begin
  1080. case ch of
  1081. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{',
  1082. ']', '}':
  1083. Result := True
  1084. else
  1085. Result := False
  1086. end;
  1087. end;
  1088. function QuoteRegExprMetaChars(const AStr: RegExprString): RegExprString;
  1089. var
  1090. i, i0, Len: integer;
  1091. ch: REChar;
  1092. begin
  1093. Result := '';
  1094. Len := Length(AStr);
  1095. i := 1;
  1096. i0 := i;
  1097. while i <= Len do
  1098. begin
  1099. ch := AStr[i];
  1100. if _IsMetaSymbol2(ch) then
  1101. begin
  1102. Result := Result + System.Copy(AStr, i0, i - i0) + EscChar + ch;
  1103. i0 := i + 1;
  1104. end;
  1105. Inc(i);
  1106. end;
  1107. Result := Result + System.Copy(AStr, i0, MaxInt); // Tail
  1108. end; { of function QuoteRegExprMetaChars
  1109. -------------------------------------------------------------- }
  1110. function RegExprSubExpressions(const ARegExpr: RegExprString; ASubExprs: TStrings;
  1111. AExtendedSyntax: boolean{$IFDEF DefParam} = False{$ENDIF}): integer;
  1112. type
  1113. TStackItemRec = record // ###0.945
  1114. SubExprIdx: integer;
  1115. StartPos: PtrInt;
  1116. end;
  1117. TStackArray = packed array [0 .. RegexMaxMaxGroups - 1] of TStackItemRec;
  1118. var
  1119. Len, SubExprLen: integer;
  1120. i, i0: integer;
  1121. Modif: TRegExprModifiers;
  1122. Stack: ^TStackArray; // ###0.945
  1123. StackIdx, StackSz: integer;
  1124. begin
  1125. Result := 0; // no unbalanced brackets found at this very moment
  1126. FillChar(Modif, SizeOf(Modif), 0);
  1127. ASubExprs.Clear; // I don't think that adding to non empty list
  1128. // can be useful, so I simplified algorithm to work only with empty list
  1129. Len := Length(ARegExpr); // some optimization tricks
  1130. // first we have to calculate number of subexpression to reserve
  1131. // space in Stack array (may be we'll reserve more than needed, but
  1132. // it's faster then memory reallocation during parsing)
  1133. StackSz := 1; // add 1 for entire r.e.
  1134. for i := 1 to Len do
  1135. if ARegExpr[i] = '(' then
  1136. Inc(StackSz);
  1137. // SetLength (Stack, StackSz); //###0.945
  1138. GetMem(Stack, SizeOf(TStackItemRec) * StackSz);
  1139. try
  1140. StackIdx := 0;
  1141. i := 1;
  1142. while (i <= Len) do
  1143. begin
  1144. case ARegExpr[i] of
  1145. '(':
  1146. begin
  1147. if (i < Len) and (ARegExpr[i + 1] = '?') then
  1148. begin
  1149. // this is not subexpression, but comment or other
  1150. // Perl extension. We must check is it (?ismxrg-ismxrg)
  1151. // and change AExtendedSyntax if /x is changed.
  1152. Inc(i, 2); // skip '(?'
  1153. i0 := i;
  1154. while (i <= Len) and (ARegExpr[i] <> ')') do
  1155. Inc(i);
  1156. if i > Len then
  1157. Result := -1 // unbalansed '('
  1158. else
  1159. if ParseModifiers(@ARegExpr[i0], i - i0, Modif) then
  1160. // Alexey-T: original code had copy from i, not from i0
  1161. AExtendedSyntax := Modif.X;
  1162. end
  1163. else
  1164. begin // subexpression starts
  1165. ASubExprs.Add(''); // just reserve space
  1166. with Stack[StackIdx] do
  1167. begin
  1168. SubExprIdx := ASubExprs.Count - 1;
  1169. StartPos := i;
  1170. end;
  1171. Inc(StackIdx);
  1172. end;
  1173. end;
  1174. ')':
  1175. begin
  1176. if StackIdx = 0 then
  1177. Result := i // unbalanced ')'
  1178. else
  1179. begin
  1180. Dec(StackIdx);
  1181. with Stack[StackIdx] do
  1182. begin
  1183. SubExprLen := i - StartPos + 1;
  1184. ASubExprs.Objects[SubExprIdx] :=
  1185. TObject(StartPos or (SubExprLen ShL 16));
  1186. ASubExprs[SubExprIdx] := System.Copy(ARegExpr, StartPos + 1,
  1187. SubExprLen - 2); // add without brackets
  1188. end;
  1189. end;
  1190. end;
  1191. EscChar:
  1192. Inc(i); // skip quoted symbol
  1193. '[':
  1194. begin
  1195. // we have to skip character ranges at once, because they can
  1196. // contain '#', and '#' in it must NOT be recognized as eXtended
  1197. // comment beginning!
  1198. i0 := i;
  1199. Inc(i);
  1200. if ARegExpr[i] = ']' // first ']' inside [] treated as simple AnsiChar, no need to check '['
  1201. then
  1202. Inc(i);
  1203. while (i <= Len) and (ARegExpr[i] <> ']') do
  1204. if ARegExpr[i] = EscChar // ###0.942
  1205. then
  1206. Inc(i, 2) // skip 'escaped' AnsiChar to prevent stopping at '\]'
  1207. else
  1208. Inc(i);
  1209. if (i > Len) or (ARegExpr[i] <> ']') // ###0.942
  1210. then
  1211. Result := -(i0 + 1); // unbalansed '[' //###0.942
  1212. end;
  1213. '#':
  1214. if AExtendedSyntax then
  1215. begin
  1216. // skip eXtended comments
  1217. while (i <= Len) and (ARegExpr[i] <> #$d) and (ARegExpr[i] <> #$a)
  1218. // do not use [#$d, #$a] due to Unicode compatibility
  1219. do
  1220. Inc(i);
  1221. while (i + 1 <= Len) and
  1222. ((ARegExpr[i + 1] = #$d) or (ARegExpr[i + 1] = #$a)) do
  1223. Inc(i); // attempt to work with different kinds of line separators
  1224. // now we are at the line separator that must be skipped.
  1225. end;
  1226. // here is no 'else' clause - we simply skip ordinary chars
  1227. end; // of case
  1228. Inc(i); // skip scanned AnsiChar
  1229. // ! can move after Len due to skipping quoted symbol
  1230. end;
  1231. // check brackets balance
  1232. if StackIdx <> 0 then
  1233. Result := -1; // unbalansed '('
  1234. // check if entire r.e. added
  1235. if (ASubExprs.Count = 0) or ((PtrInt(ASubExprs.Objects[0]) and $FFFF) <> 1)
  1236. or (((PtrInt(ASubExprs.Objects[0]) ShR 16) and $FFFF) <> Len)
  1237. // whole r.e. wasn't added because it isn't bracketed
  1238. // well, we add it now:
  1239. then
  1240. ASubExprs.InsertObject(0, ARegExpr, TObject((Len ShL 16) or 1));
  1241. finally
  1242. FreeMem(Stack);
  1243. end;
  1244. end; { of function RegExprSubExpressions
  1245. -------------------------------------------------------------- }
  1246. const
  1247. OP_MAGIC = TREOp(216); // programm signature
  1248. // name opcode opnd? meaning
  1249. OP_EEND = TREOp(0); // - End of program
  1250. OP_BOL = TREOp(1); // - Match "" at beginning of line
  1251. OP_EOL = TREOp(2); // - Match "" at end of line
  1252. OP_ANY = TREOp(3); // - Match any one character
  1253. OP_ANYOF = TREOp(4); // Str Match any character in string Str
  1254. OP_ANYBUT = TREOp(5); // Str Match any AnsiChar. not in string Str
  1255. OP_BRANCH = TREOp(6); // Node Match this alternative, or the next
  1256. OP_BACK = TREOp(7); // - Jump backward (Next < 0)
  1257. OP_EXACTLY = TREOp(8); // Str Match string Str
  1258. OP_NOTHING = TREOp(9); // - Match empty string
  1259. OP_STAR = TREOp(10); // Node Match this (simple) thing 0 or more times
  1260. OP_PLUS = TREOp(11); // Node Match this (simple) thing 1 or more times
  1261. OP_ANYDIGIT = TREOp(12); // - Match any digit (equiv [0-9])
  1262. OP_NOTDIGIT = TREOp(13); // - Match not digit (equiv [0-9])
  1263. OP_ANYLETTER = TREOp(14); // - Match any letter from property WordChars
  1264. OP_NOTLETTER = TREOp(15); // - Match not letter from property WordChars
  1265. OP_ANYSPACE = TREOp(16); // - Match any space AnsiChar (see property SpaceChars)
  1266. OP_NOTSPACE = TREOp(17); // - Match not space AnsiChar (see property SpaceChars)
  1267. OP_BRACES = TREOp(18);
  1268. // Node,Min,Max Match this (simple) thing from Min to Max times.
  1269. // Min and Max are TREBracesArg
  1270. OP_COMMENT = TREOp(19); // - Comment ;)
  1271. OP_EXACTLYCI = TREOp(20); // Str Match string Str case insensitive
  1272. OP_ANYOFCI = TREOp(21);
  1273. // Str Match any character in string Str, case insensitive
  1274. OP_ANYBUTCI = TREOp(22);
  1275. // Str Match any AnsiChar. not in string Str, case insensitive
  1276. OP_LOOPENTRY = TREOp(23); // Node Start of loop (Node - LOOP for this loop)
  1277. OP_LOOP = TREOp(24); // Node,Min,Max,LoopEntryJmp - back jump for LOOPENTRY.
  1278. // Min and Max are TREBracesArg
  1279. // Node - next node in sequence,
  1280. // LoopEntryJmp - associated LOOPENTRY node addr
  1281. OP_EOL2 = TReOp(25); // like OP_EOL but also matches before final line-break
  1282. OP_BSUBEXP = TREOp(28);
  1283. // Idx Match previously matched subexpression #Idx (stored as REChar) //###0.936
  1284. OP_BSUBEXPCI = TREOp(29); // Idx -"- in case-insensitive mode
  1285. // Non-Greedy Style Ops //###0.940
  1286. OP_STARNG = TREOp(30); // Same as OP_START but in non-greedy mode
  1287. OP_PLUSNG = TREOp(31); // Same as OP_PLUS but in non-greedy mode
  1288. OP_BRACESNG = TREOp(32); // Same as OP_BRACES but in non-greedy mode
  1289. OP_LOOPNG = TREOp(33); // Same as OP_LOOP but in non-greedy mode
  1290. // Multiline mode \m
  1291. OP_BOLML = TREOp(34); // - Match "" at beginning of line
  1292. OP_EOLML = TREOp(35); // - Match "" at end of line
  1293. OP_ANYML = TREOp(36); // - Match any one character
  1294. // Word boundary
  1295. OP_BOUND = TREOp(37); // Match "" between words //###0.943
  1296. OP_NOTBOUND = TREOp(38); // Match "" not between words //###0.943
  1297. OP_ANYHORZSEP = TREOp(39); // Any horizontal whitespace \h
  1298. OP_NOTHORZSEP = TREOp(40); // Not horizontal whitespace \H
  1299. OP_ANYVERTSEP = TREOp(41); // Any vertical whitespace \v
  1300. OP_NOTVERTSEP = TREOp(42); // Not vertical whitespace \V
  1301. OP_ANYCATEGORY = TREOp(43); // \p{L}
  1302. OP_NOTCATEGORY = TREOp(44); // \P{L}
  1303. OP_STAR_POSS = TReOp(45);
  1304. OP_PLUS_POSS = TReOp(46);
  1305. OP_BRACES_POSS = TReOp(47);
  1306. OP_RECUR = TReOp(48);
  1307. // !!! Change OP_OPEN value if you add new opcodes !!!
  1308. OP_OPEN = TREOp(50); // Opening of group; OP_OPEN+i is for group i
  1309. OP_OPEN_FIRST = Succ(OP_OPEN);
  1310. OP_OPEN_LAST = TREOp(Ord(OP_OPEN) + RegexMaxGroups - 1);
  1311. OP_CLOSE = Succ(OP_OPEN_LAST); // Closing of group; OP_CLOSE+i is for group i
  1312. OP_CLOSE_FIRST = Succ(OP_CLOSE);
  1313. OP_CLOSE_LAST = TReOp(Ord(OP_CLOSE) + RegexMaxGroups - 1);
  1314. OP_SUBCALL = Succ(OP_CLOSE_LAST); // Call of subroutine; OP_SUBCALL+i is for group i
  1315. OP_SUBCALL_FIRST = Succ(OP_SUBCALL);
  1316. OP_SUBCALL_LAST =
  1317. {$IFDEF UnicodeRE}
  1318. TReOp(Ord(OP_SUBCALL) + RegexMaxGroups - 1);
  1319. {$ELSE}
  1320. High(REChar); // must fit to 0..255 range
  1321. {$ENDIF}
  1322. // We work with p-code through pointers, compatible with PRegExprChar.
  1323. // Note: all code components (TRENextOff, TREOp, TREBracesArg, etc)
  1324. // must have lengths that can be divided by SizeOf (REChar) !
  1325. // A node is TREOp of opcode followed Next "pointer" of TRENextOff type.
  1326. // The Next is a offset from the opcode of the node containing it.
  1327. // An operand, if any, simply follows the node. (Note that much of
  1328. // the code generation knows about this implicit relationship!)
  1329. // Using TRENextOff=PtrInt speed up p-code processing.
  1330. // Opcodes description:
  1331. //
  1332. // BRANCH The set of branches constituting a single choice are hooked
  1333. // together with their "next" pointers, since precedence prevents
  1334. // anything being concatenated to any individual branch. The
  1335. // "next" pointer of the last BRANCH in a choice points to the
  1336. // thing following the whole choice. This is also where the
  1337. // final "next" pointer of each individual branch points; each
  1338. // branch starts with the operand node of a BRANCH node.
  1339. // BACK Normal "next" pointers all implicitly point forward; BACK
  1340. // exists to make loop structures possible.
  1341. // STAR,PLUS,BRACES '?', and complex '*' and '+', are implemented as
  1342. // circular BRANCH structures using BACK. Complex '{min,max}'
  1343. // - as pair LOOPENTRY-LOOP (see below). Simple cases (one
  1344. // character per match) are implemented with STAR, PLUS and
  1345. // BRACES for speed and to minimize recursive plunges.
  1346. // LOOPENTRY,LOOP {min,max} are implemented as special pair
  1347. // LOOPENTRY-LOOP. Each LOOPENTRY initialize loopstack for
  1348. // current level.
  1349. // OPEN,CLOSE are numbered at compile time.
  1350. { ============================================================= }
  1351. { ================== Error handling section =================== }
  1352. { ============================================================= }
  1353. const
  1354. reeOk = 0;
  1355. reeCompNullArgument = 100;
  1356. reeUnknownMetaSymbol = 101;
  1357. reeCompParseRegTooManyBrackets = 102;
  1358. reeCompParseRegUnmatchedBrackets = 103;
  1359. reeCompParseRegUnmatchedBrackets2 = 104;
  1360. reeCompParseRegJunkOnEnd = 105;
  1361. reePlusStarOperandCouldBeEmpty = 106;
  1362. reeNestedQuantif = 107;
  1363. reeBadHexDigit = 108;
  1364. reeInvalidRange = 109;
  1365. reeParseAtomTrailingBackSlash = 110;
  1366. reeNoHexCodeAfterBSlashX = 111;
  1367. reeHexCodeAfterBSlashXTooBig = 112;
  1368. reeUnmatchedSqBrackets = 113;
  1369. reeInternalUrp = 114;
  1370. reeQuantifFollowsNothing = 115;
  1371. reeTrailingBackSlash = 116;
  1372. reeNoLetterAfterBSlashC = 117;
  1373. reeMetaCharAfterMinusInRange = 118;
  1374. reeRarseAtomInternalDisaster = 119;
  1375. reeIncorrectSpecialBrackets = 120;
  1376. reeIncorrectBraces = 121;
  1377. reeBRACESArgTooBig = 122;
  1378. reeUnknownOpcodeInFillFirst = 123;
  1379. reeBracesMinParamGreaterMax = 124;
  1380. reeUnclosedComment = 125;
  1381. reeComplexBracesNotImplemented = 126;
  1382. reeUnrecognizedModifier = 127;
  1383. reeBadLinePairedSeparator = 128;
  1384. reeBadUnicodeCategory = 129;
  1385. reeTooSmallCheckersArray = 130;
  1386. reePossessiveAfterComplexBraces = 131;
  1387. reeBadRecursion = 132;
  1388. reeBadSubCall = 133;
  1389. reeNamedGroupBad = 140;
  1390. reeNamedGroupBadName = 141;
  1391. reeNamedGroupBadRef = 142;
  1392. reeNamedGroupDupName = 143;
  1393. reeLookaheadBad = 150;
  1394. reeLookbehindBad = 152;
  1395. reeLookbehindTooComplex = 153;
  1396. reeLookaroundNotAtEdge = 154;
  1397. // Runtime errors must be >= reeFirstRuntimeCode
  1398. reeFirstRuntimeCode = 1000;
  1399. reeRegRepeatCalledInappropriately = 1000;
  1400. reeMatchPrimMemoryCorruption = 1001;
  1401. reeMatchPrimCorruptedPointers = 1002;
  1402. reeNoExpression = 1003;
  1403. reeCorruptedProgram = 1004;
  1404. reeOffsetMustBePositive = 1006;
  1405. reeExecNextWithoutExec = 1007;
  1406. reeBadOpcodeInCharClass = 1008;
  1407. reeDumpCorruptedOpcode = 1011;
  1408. reeModifierUnsupported = 1013;
  1409. reeLoopStackExceeded = 1014;
  1410. reeLoopWithoutEntry = 1015;
  1411. function TRegExpr.ErrorMsg(AErrorID: integer): RegExprString;
  1412. begin
  1413. case AErrorID of
  1414. reeOk:
  1415. Result := 'No errors';
  1416. reeCompNullArgument:
  1417. Result := 'TRegExpr compile: null argument';
  1418. reeUnknownMetaSymbol:
  1419. Result := 'TRegExpr compile: unknown meta-character: \' + fLastErrorSymbol;
  1420. reeCompParseRegTooManyBrackets:
  1421. Result := 'TRegExpr compile: ParseReg: too many ()';
  1422. reeCompParseRegUnmatchedBrackets:
  1423. Result := 'TRegExpr compile: ParseReg: unmatched ()';
  1424. reeCompParseRegUnmatchedBrackets2:
  1425. Result := 'TRegExpr compile: ParseReg: unmatched ()';
  1426. reeCompParseRegJunkOnEnd:
  1427. Result := 'TRegExpr compile: ParseReg: junk at end';
  1428. reePlusStarOperandCouldBeEmpty:
  1429. Result := 'TRegExpr compile: *+ operand could be empty';
  1430. reeNestedQuantif:
  1431. Result := 'TRegExpr compile: nested quantifier *?+';
  1432. reeBadHexDigit:
  1433. Result := 'TRegExpr compile: bad hex digit';
  1434. reeInvalidRange:
  1435. Result := 'TRegExpr compile: invalid [] range';
  1436. reeParseAtomTrailingBackSlash:
  1437. Result := 'TRegExpr compile: parse atom trailing \';
  1438. reeNoHexCodeAfterBSlashX:
  1439. Result := 'TRegExpr compile: no hex code after \x';
  1440. reeNoLetterAfterBSlashC:
  1441. Result := 'TRegExpr compile: no letter "A".."Z" after \c';
  1442. reeMetaCharAfterMinusInRange:
  1443. Result := 'TRegExpr compile: metachar after "-" in [] range';
  1444. reeHexCodeAfterBSlashXTooBig:
  1445. Result := 'TRegExpr compile: hex code after \x is too big';
  1446. reeUnmatchedSqBrackets:
  1447. Result := 'TRegExpr compile: unmatched []';
  1448. reeInternalUrp:
  1449. Result := 'TRegExpr compile: internal fail on AnsiChar "|", ")"';
  1450. reeQuantifFollowsNothing:
  1451. Result := 'TRegExpr compile: quantifier ?+*{ follows nothing';
  1452. reeTrailingBackSlash:
  1453. Result := 'TRegExpr compile: trailing \';
  1454. reeRarseAtomInternalDisaster:
  1455. Result := 'TRegExpr compile: RarseAtom internal disaster';
  1456. reeIncorrectSpecialBrackets:
  1457. Result := 'TRegExpr compile: incorrect expression in (?...) brackets';
  1458. reeIncorrectBraces:
  1459. Result := 'TRegExpr compile: incorrect {} braces';
  1460. reeBRACESArgTooBig:
  1461. Result := 'TRegExpr compile: braces {} argument too big';
  1462. reeUnknownOpcodeInFillFirst:
  1463. Result := 'TRegExpr compile: unknown opcode in FillFirstCharSet ('+DumpOp(fLastErrorOpcode)+')';
  1464. reeBracesMinParamGreaterMax:
  1465. Result := 'TRegExpr compile: braces {} min param greater then max';
  1466. reeUnclosedComment:
  1467. Result := 'TRegExpr compile: unclosed (?#comment)';
  1468. reeComplexBracesNotImplemented:
  1469. Result := 'TRegExpr compile: if you use braces {} and non-greedy ops *?, +?, ?? for complex cases, enable {$DEFINE ComplexBraces}';
  1470. reeUnrecognizedModifier:
  1471. Result := 'TRegExpr compile: incorrect modifier in (?...)';
  1472. reeBadLinePairedSeparator:
  1473. Result := 'TRegExpr compile: LinePairedSeparator must countain two different chars or be empty';
  1474. reeBadUnicodeCategory:
  1475. Result := 'TRegExpr compile: invalid category after \p or \P';
  1476. reeTooSmallCheckersArray:
  1477. Result := 'TRegExpr compile: too small CharCheckers array';
  1478. reePossessiveAfterComplexBraces:
  1479. Result := 'TRegExpr compile: possessive + after complex braces: (foo){n,m}+';
  1480. reeBadRecursion:
  1481. Result := 'TRegExpr compile: bad recursion (?R)';
  1482. reeBadSubCall:
  1483. Result := 'TRegExpr compile: bad subroutine call';
  1484. reeNamedGroupBad:
  1485. Result := 'TRegExpr compile: bad named group';
  1486. reeNamedGroupBadName:
  1487. Result := 'TRegExpr compile: bad identifier in named group';
  1488. reeNamedGroupBadRef:
  1489. Result := 'TRegExpr compile: bad back-reference to named group';
  1490. reeNamedGroupDupName:
  1491. Result := 'TRegExpr compile: named group defined more than once';
  1492. reeLookaheadBad:
  1493. Result := 'TRegExpr compile: bad lookahead';
  1494. reeLookbehindBad:
  1495. Result := 'TRegExpr compile: bad lookbehind';
  1496. reeLookbehindTooComplex:
  1497. Result := 'TRegExpr compile: lookbehind (?<!foo) must have fixed length';
  1498. reeLookaroundNotAtEdge:
  1499. Result := 'TRegExpr compile: lookaround brackets must be at the very beginning/ending';
  1500. reeRegRepeatCalledInappropriately:
  1501. Result := 'TRegExpr exec: RegRepeat called inappropriately';
  1502. reeMatchPrimMemoryCorruption:
  1503. Result := 'TRegExpr exec: MatchPrim memory corruption';
  1504. reeMatchPrimCorruptedPointers:
  1505. Result := 'TRegExpr exec: MatchPrim corrupted pointers';
  1506. reeNoExpression:
  1507. Result := 'TRegExpr exec: empty expression';
  1508. reeCorruptedProgram:
  1509. Result := 'TRegExpr exec: corrupted opcode (no magic byte)';
  1510. reeOffsetMustBePositive:
  1511. Result := 'TRegExpr exec: offset must be >0';
  1512. reeExecNextWithoutExec:
  1513. Result := 'TRegExpr exec: ExecNext without Exec(Pos)';
  1514. reeBadOpcodeInCharClass:
  1515. Result := 'TRegExpr exec: invalid opcode in AnsiChar class';
  1516. reeDumpCorruptedOpcode:
  1517. Result := 'TRegExpr dump: corrupted opcode';
  1518. reeLoopStackExceeded:
  1519. Result := 'TRegExpr exec: loop stack exceeded';
  1520. reeLoopWithoutEntry:
  1521. Result := 'TRegExpr exec: loop without loop entry';
  1522. else
  1523. Result := 'Unknown error';
  1524. end;
  1525. end; { of procedure TRegExpr.Error
  1526. -------------------------------------------------------------- }
  1527. function TRegExpr.LastError: integer;
  1528. begin
  1529. Result := fLastError;
  1530. fLastError := reeOk;
  1531. end; { of function TRegExpr.LastError
  1532. -------------------------------------------------------------- }
  1533. { ============================================================= }
  1534. { ===================== Common section ======================== }
  1535. { ============================================================= }
  1536. class function TRegExpr.VersionMajor: integer;
  1537. begin
  1538. Result := REVersionMajor;
  1539. end;
  1540. class function TRegExpr.VersionMinor: integer;
  1541. begin
  1542. Result := REVersionMinor;
  1543. end;
  1544. constructor TRegExpr.Create;
  1545. begin
  1546. inherited;
  1547. programm := nil;
  1548. fExpression := '';
  1549. fInputString := '';
  1550. FillChar(fModifiers, SizeOf(fModifiers), 0);
  1551. fModifiers.I := RegExprModifierI;
  1552. fModifiers.R := RegExprModifierR;
  1553. fModifiers.S := RegExprModifierS;
  1554. fModifiers.G := RegExprModifierG;
  1555. fModifiers.M := RegExprModifierM;
  1556. fModifiers.X := RegExprModifierX;
  1557. {$IFDEF UseSpaceChars}
  1558. SpaceChars := RegExprSpaceChars;
  1559. {$ENDIF}
  1560. {$IFDEF UseWordChars}
  1561. WordChars := RegExprWordChars;
  1562. {$ENDIF}
  1563. {$IFDEF UseLineSep}
  1564. fLineSeparators := RegExprLineSeparators;
  1565. {$ENDIF}
  1566. fUsePairedBreak := RegExprUsePairedBreak;
  1567. fReplaceLineEnd := RegExprReplaceLineBreak;
  1568. fSlowChecksSizeMax := 2000;
  1569. {$IFDEF UseLineSep}
  1570. InitLineSepArray;
  1571. {$ENDIF}
  1572. InitCharCheckers;
  1573. {$IFDEF Compat}
  1574. fInvertCase := OldInvertCase;
  1575. {$ENDIF}
  1576. end; { of constructor TRegExpr.Create
  1577. -------------------------------------------------------------- }
  1578. {$IFDEF OverMeth}
  1579. constructor TRegExpr.Create(const AExpression: RegExprString);
  1580. begin
  1581. Create;
  1582. Expression := AExpression;
  1583. end;
  1584. {$ENDIF}
  1585. destructor TRegExpr.Destroy;
  1586. begin
  1587. if programm <> nil then
  1588. begin
  1589. FreeMem(programm);
  1590. programm := nil;
  1591. end;
  1592. if Assigned(fHelper) then
  1593. FreeAndNil(fHelper);
  1594. end;
  1595. procedure TRegExpr.SetExpression(const AStr: RegExprString);
  1596. begin
  1597. if (AStr <> fExpression) or not IsCompiled then
  1598. begin
  1599. fExpression := AStr;
  1600. UniqueString(fExpression);
  1601. fRegexStart := PRegExprChar(fExpression);
  1602. fRegexEnd := fRegexStart + Length(fExpression);
  1603. InvalidateProgramm;
  1604. end;
  1605. end; { of procedure TRegExpr.SetExpression
  1606. -------------------------------------------------------------- }
  1607. function TRegExpr.GetSubExprCount: integer;
  1608. begin
  1609. // if nothing found, we must return -1 per TRegExpr docs
  1610. if GrpBounds[0].GrpStart[0] = nil then
  1611. Result := -1
  1612. else
  1613. Result := GrpCount;
  1614. end;
  1615. function TRegExpr.GetMatchPos(Idx: integer): PtrInt;
  1616. begin
  1617. Idx := GrpIndexes[Idx];
  1618. if (Idx >= 0) and (GrpBounds[0].GrpStart[Idx] <> nil) then
  1619. Result := GrpBounds[0].GrpStart[Idx] - fInputStart + 1
  1620. else
  1621. Result := -1;
  1622. end; { of function TRegExpr.GetMatchPos
  1623. -------------------------------------------------------------- }
  1624. function TRegExpr.GetMatchLen(Idx: integer): PtrInt;
  1625. begin
  1626. Idx := GrpIndexes[Idx];
  1627. if (Idx >= 0) and (GrpBounds[0].GrpStart[Idx] <> nil) then
  1628. Result := GrpBounds[0].GrpEnd[Idx] - GrpBounds[0].GrpStart[Idx]
  1629. else
  1630. Result := -1;
  1631. end; { of function TRegExpr.GetMatchLen
  1632. -------------------------------------------------------------- }
  1633. function TRegExpr.GetMatch(Idx: integer): RegExprString;
  1634. begin
  1635. Result := '';
  1636. Idx := GrpIndexes[Idx];
  1637. if (Idx >= 0) and (GrpBounds[0].GrpEnd[Idx] > GrpBounds[0].GrpStart[Idx]) then
  1638. SetString(Result, GrpBounds[0].GrpStart[Idx], GrpBounds[0].GrpEnd[Idx] - GrpBounds[0].GrpStart[Idx]);
  1639. end; { of function TRegExpr.GetMatch
  1640. -------------------------------------------------------------- }
  1641. function TRegExpr.MatchIndexFromName(const AName: RegExprString): integer;
  1642. var
  1643. i: integer;
  1644. begin
  1645. for i := 1 {not 0} to GrpCount do
  1646. if GrpNames[i] = AName then
  1647. begin
  1648. Result := i;
  1649. Exit;
  1650. end;
  1651. Result := -1;
  1652. end;
  1653. function TRegExpr.MatchFromName(const AName: RegExprString): RegExprString;
  1654. var
  1655. Idx: integer;
  1656. begin
  1657. Idx := MatchIndexFromName(AName);
  1658. if Idx >= 0 then
  1659. Result := GetMatch(Idx)
  1660. else
  1661. Result := '';
  1662. end;
  1663. function TRegExpr.GetModifierStr: RegExprString;
  1664. begin
  1665. Result := '-';
  1666. if ModifierI then
  1667. Result := 'i' + Result
  1668. else
  1669. Result := Result + 'i';
  1670. if ModifierR then
  1671. Result := 'r' + Result
  1672. else
  1673. Result := Result + 'r';
  1674. if ModifierS then
  1675. Result := 's' + Result
  1676. else
  1677. Result := Result + 's';
  1678. if ModifierG then
  1679. Result := 'g' + Result
  1680. else
  1681. Result := Result + 'g';
  1682. if ModifierM then
  1683. Result := 'm' + Result
  1684. else
  1685. Result := Result + 'm';
  1686. if ModifierX then
  1687. Result := 'x' + Result
  1688. else
  1689. Result := Result + 'x';
  1690. if Result[Length(Result)] = '-' // remove '-' if all modifiers are 'On'
  1691. then
  1692. System.Delete(Result, Length(Result), 1);
  1693. end; { of function TRegExpr.GetModifierStr
  1694. -------------------------------------------------------------- }
  1695. procedure TRegExpr.SetModifierG(AValue: boolean);
  1696. begin
  1697. if fModifiers.G <> AValue then
  1698. begin
  1699. fModifiers.G := AValue;
  1700. InvalidateProgramm;
  1701. end;
  1702. end;
  1703. procedure TRegExpr.SetModifierI(AValue: boolean);
  1704. begin
  1705. if fModifiers.I <> AValue then
  1706. begin
  1707. fModifiers.I := AValue;
  1708. InvalidateProgramm;
  1709. end;
  1710. end;
  1711. procedure TRegExpr.SetModifierM(AValue: boolean);
  1712. begin
  1713. if fModifiers.M <> AValue then
  1714. begin
  1715. fModifiers.M := AValue;
  1716. InvalidateProgramm;
  1717. end;
  1718. end;
  1719. procedure TRegExpr.SetModifierR(AValue: boolean);
  1720. begin
  1721. if fModifiers.R <> AValue then
  1722. begin
  1723. fModifiers.R := AValue;
  1724. InvalidateProgramm;
  1725. end;
  1726. end;
  1727. procedure TRegExpr.SetModifierS(AValue: boolean);
  1728. begin
  1729. if fModifiers.S <> AValue then
  1730. begin
  1731. fModifiers.S := AValue;
  1732. InvalidateProgramm;
  1733. end;
  1734. end;
  1735. procedure TRegExpr.SetModifierX(AValue: boolean);
  1736. begin
  1737. if fModifiers.X <> AValue then
  1738. begin
  1739. fModifiers.X := AValue;
  1740. InvalidateProgramm;
  1741. end;
  1742. end;
  1743. procedure TRegExpr.SetModifierStr(const AStr: RegExprString);
  1744. begin
  1745. if ParseModifiers(PRegExprChar(AStr), Length(AStr), fModifiers) then
  1746. InvalidateProgramm
  1747. else
  1748. Error(reeModifierUnsupported);
  1749. end;
  1750. { ============================================================= }
  1751. { ==================== Compiler section ======================= }
  1752. { ============================================================= }
  1753. {$IFDEF FastUnicodeData}
  1754. function TRegExpr.IsWordChar(AChar: REChar): boolean;
  1755. begin
  1756. // bit 7 in value: is word AnsiChar
  1757. Result := CharCategoryArray[Ord(AChar)] and 128 <> 0;
  1758. end;
  1759. (*
  1760. // Unicode General Category
  1761. UGC_UppercaseLetter = 0; Lu
  1762. UGC_LowercaseLetter = 1; Ll
  1763. UGC_TitlecaseLetter = 2; Lt
  1764. UGC_ModifierLetter = 3; Lm
  1765. UGC_OtherLetter = 4; Lo
  1766. UGC_NonSpacingMark = 5; Mn
  1767. UGC_CombiningMark = 6; Mc
  1768. UGC_EnclosingMark = 7; Me
  1769. UGC_DecimalNumber = 8; Nd
  1770. UGC_LetterNumber = 9; Nl
  1771. UGC_OtherNumber = 10; No
  1772. UGC_ConnectPunctuation = 11; Pc
  1773. UGC_DashPunctuation = 12; Pd
  1774. UGC_OpenPunctuation = 13; Ps
  1775. UGC_ClosePunctuation = 14; Pe
  1776. UGC_InitialPunctuation = 15; Pi
  1777. UGC_FinalPunctuation = 16; Pf
  1778. UGC_OtherPunctuation = 17; Po
  1779. UGC_MathSymbol = 18; Sm
  1780. UGC_CurrencySymbol = 19; Sc
  1781. UGC_ModifierSymbol = 20; Sk
  1782. UGC_OtherSymbol = 21; So
  1783. UGC_SpaceSeparator = 22; Zs
  1784. UGC_LineSeparator = 23; Zl
  1785. UGC_ParagraphSeparator = 24; Zp
  1786. UGC_Control = 25; Cc
  1787. UGC_Format = 26; Cf
  1788. UGC_Surrogate = 27; Cs
  1789. UGC_PrivateUse = 28; Co
  1790. UGC_Unassigned = 29; Cn
  1791. *)
  1792. const
  1793. CategoryNames: array[0..29] of array[0..1] of REChar = (
  1794. ('L', 'u'),
  1795. ('L', 'l'),
  1796. ('L', 't'),
  1797. ('L', 'm'),
  1798. ('L', 'o'),
  1799. ('M', 'n'),
  1800. ('M', 'c'),
  1801. ('M', 'e'),
  1802. ('N', 'd'),
  1803. ('N', 'l'),
  1804. ('N', 'o'),
  1805. ('P', 'c'),
  1806. ('P', 'd'),
  1807. ('P', 's'),
  1808. ('P', 'e'),
  1809. ('P', 'i'),
  1810. ('P', 'f'),
  1811. ('P', 'o'),
  1812. ('S', 'm'),
  1813. ('S', 'c'),
  1814. ('S', 'k'),
  1815. ('S', 'o'),
  1816. ('Z', 's'),
  1817. ('Z', 'l'),
  1818. ('Z', 'p'),
  1819. ('C', 'c'),
  1820. ('C', 'f'),
  1821. ('C', 's'),
  1822. ('C', 'o'),
  1823. ('C', 'n')
  1824. );
  1825. function IsCategoryFirstChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1826. begin
  1827. case AChar of
  1828. 'L', 'M', 'N', 'P', 'S', 'C', 'Z':
  1829. Result := True;
  1830. else
  1831. Result := False;
  1832. end;
  1833. end;
  1834. function IsCategoryChars(AChar, AChar2: REChar): boolean;
  1835. var
  1836. i: integer;
  1837. begin
  1838. for i := Low(CategoryNames) to High(CategoryNames) do
  1839. if (AChar = CategoryNames[i][0]) then
  1840. if (AChar2 = CategoryNames[i][1]) then
  1841. begin
  1842. Result := True;
  1843. Exit
  1844. end;
  1845. Result := False;
  1846. end;
  1847. function CheckCharCategory(AChar: REChar; Ch0, Ch1: REChar): boolean;
  1848. // AChar: check this AnsiChar against opcode
  1849. // Ch0, Ch1: opcode operands after OP_*CATEGORY
  1850. var
  1851. N: byte;
  1852. Name0, Name1: REChar;
  1853. begin
  1854. Result := False;
  1855. // bits 0..6 are category
  1856. N := CharCategoryArray[Ord(AChar)] and 127;
  1857. if N <= High(CategoryNames) then
  1858. begin
  1859. Name0 := CategoryNames[N][0];
  1860. Name1 := CategoryNames[N][1];
  1861. if Ch0 <> Name0 then Exit;
  1862. if Ch1 <> #0 then
  1863. if Ch1 <> Name1 then Exit;
  1864. Result := True;
  1865. end;
  1866. end;
  1867. function MatchOneCharCategory(opnd, scan: PRegExprChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1868. // opnd: points to opcode operands after OP_*CATEGORY
  1869. // scan: points into InputString
  1870. begin
  1871. Result := CheckCharCategory(scan^, opnd^, (opnd + 1)^);
  1872. end;
  1873. {$ELSE}
  1874. function TRegExpr.IsWordChar(AChar: REChar): boolean;
  1875. begin
  1876. {$IFDEF UseWordChars}
  1877. Result := Pos(AChar, fWordChars) > 0;
  1878. {$ELSE}
  1879. case AChar of
  1880. 'a' .. 'z',
  1881. 'A' .. 'Z',
  1882. '0' .. '9', '_':
  1883. Result := True
  1884. else
  1885. Result := False;
  1886. end;
  1887. {$ENDIF}
  1888. end;
  1889. {$ENDIF}
  1890. function TRegExpr.IsSpaceChar(AChar: REChar): boolean;
  1891. begin
  1892. {$IFDEF UseSpaceChars}
  1893. Result := Pos(AChar, fSpaceChars) > 0;
  1894. {$ELSE}
  1895. case AChar of
  1896. ' ', #$9, #$A, #$D, #$C:
  1897. Result := True
  1898. else
  1899. Result := False;
  1900. end;
  1901. {$ENDIF}
  1902. end;
  1903. function TRegExpr.IsCustomLineSeparator(AChar: REChar): boolean;
  1904. begin
  1905. {$IFDEF UseLineSep}
  1906. {$IFDEF UnicodeRE}
  1907. Result := Pos(AChar, fLineSeparators) > 0;
  1908. {$ELSE}
  1909. Result := fLineSepArray[byte(AChar)];
  1910. {$ENDIF}
  1911. {$ELSE}
  1912. case AChar of
  1913. #$d, #$a,
  1914. {$IFDEF UnicodeRE}
  1915. #$85, #$2028, #$2029,
  1916. {$ENDIF}
  1917. #$b, #$c:
  1918. Result := True;
  1919. else
  1920. Result := False;
  1921. end;
  1922. {$ENDIF}
  1923. end;
  1924. function IsDigitChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1925. begin
  1926. case AChar of
  1927. '0' .. '9':
  1928. Result := True;
  1929. else
  1930. Result := False;
  1931. end;
  1932. end;
  1933. function IsHorzSeparator(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1934. begin
  1935. // Tab and Unicode categoty "Space Separator": https://www.compart.com/en/unicode/category/Zs
  1936. case AChar of
  1937. #9, #$20, #$A0:
  1938. Result := True;
  1939. {$IFDEF UnicodeRE}
  1940. #$1680, #$2000 .. #$200A, #$202F, #$205F, #$3000:
  1941. Result := True;
  1942. {$ENDIF}
  1943. else
  1944. Result := False;
  1945. end;
  1946. end;
  1947. function IsVertLineSeparator(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1948. begin
  1949. case AChar of
  1950. #$d, #$a, #$b, #$c:
  1951. Result := True;
  1952. {$IFDEF UnicodeRE}
  1953. #$2028, #$2029, #$85:
  1954. Result := True;
  1955. {$ENDIF}
  1956. else
  1957. Result := False;
  1958. end;
  1959. end;
  1960. procedure TRegExpr.InvalidateProgramm;
  1961. begin
  1962. if programm <> nil then
  1963. begin
  1964. FreeMem(programm);
  1965. programm := nil;
  1966. end;
  1967. end; { of procedure TRegExpr.InvalidateProgramm
  1968. -------------------------------------------------------------- }
  1969. procedure TRegExpr.Compile;
  1970. begin
  1971. if fExpression = '' then
  1972. begin
  1973. Error(reeNoExpression);
  1974. Exit;
  1975. end;
  1976. CompileRegExpr(fRegexStart);
  1977. end; { of procedure TRegExpr.Compile
  1978. -------------------------------------------------------------- }
  1979. {$IFDEF UseLineSep}
  1980. procedure TRegExpr.InitLineSepArray;
  1981. {$IFNDEF UnicodeRE}
  1982. var
  1983. i: integer;
  1984. {$ENDIF}
  1985. begin
  1986. {$IFNDEF UnicodeRE}
  1987. FillChar(fLineSepArray, SizeOf(fLineSepArray), 0);
  1988. for i := 1 to Length(fLineSeparators) do
  1989. fLineSepArray[byte(fLineSeparators[i])] := True;
  1990. {$ENDIF}
  1991. end;
  1992. {$ENDIF}
  1993. function TRegExpr.IsProgrammOk: boolean;
  1994. begin
  1995. Result := False;
  1996. // check modifiers
  1997. if not IsModifiersEqual(fModifiers, fProgModifiers) then
  1998. InvalidateProgramm;
  1999. // compile if needed
  2000. if programm = nil then
  2001. begin
  2002. Compile;
  2003. // Check compiled programm
  2004. if programm = nil then
  2005. Exit;
  2006. end;
  2007. if programm[0] <> OP_MAGIC then
  2008. Error(reeCorruptedProgram)
  2009. else
  2010. Result := True;
  2011. end; { of function TRegExpr.IsProgrammOk
  2012. -------------------------------------------------------------- }
  2013. procedure TRegExpr.Tail(p: PRegExprChar; val: PRegExprChar);
  2014. // set the next-pointer at the end of a node chain
  2015. var
  2016. scan: PRegExprChar;
  2017. temp: PRegExprChar;
  2018. begin
  2019. if p = @regDummy then
  2020. Exit;
  2021. // Find last node.
  2022. scan := p;
  2023. repeat
  2024. temp := regNext(scan);
  2025. if temp = nil then
  2026. Break;
  2027. scan := temp;
  2028. until False;
  2029. // Set Next 'pointer'
  2030. if val < scan then
  2031. PRENextOff(AlignToPtr(scan + REOpSz))^ := -(scan - val) // ###0.948
  2032. // work around PWideChar subtraction bug (Delphi uses
  2033. // shr after subtraction to calculate widechar distance %-( )
  2034. // so, if difference is negative we have .. the "feature" :(
  2035. // I could wrap it in $IFDEF UnicodeRE, but I didn't because
  2036. // "P – Q computes the difference between the address given
  2037. // by P (the higher address) and the address given by Q (the
  2038. // lower address)" - Delphi help quotation.
  2039. else
  2040. PRENextOff(AlignToPtr(scan + REOpSz))^ := val - scan; // ###0.933
  2041. end; { of procedure TRegExpr.Tail
  2042. -------------------------------------------------------------- }
  2043. procedure TRegExpr.OpTail(p: PRegExprChar; val: PRegExprChar);
  2044. // regtail on operand of first argument; nop if operandless
  2045. begin
  2046. // "Operandless" and "op != OP_BRANCH" are synonymous in practice.
  2047. if (p = nil) or (p = @regDummy) or (PREOp(p)^ <> OP_BRANCH) then
  2048. Exit;
  2049. Tail(p + REOpSz + RENextOffSz, val); // ###0.933
  2050. end; { of procedure TRegExpr.OpTail
  2051. -------------------------------------------------------------- }
  2052. function TRegExpr.EmitNode(op: TREOp): PRegExprChar; // ###0.933
  2053. // emit a node, return location
  2054. begin
  2055. Result := regCode;
  2056. if Result <> @regDummy then
  2057. begin
  2058. PREOp(regCode)^ := op;
  2059. Inc(regCode, REOpSz);
  2060. PRENextOff(AlignToPtr(regCode))^ := 0; // Next "pointer" := nil
  2061. Inc(regCode, RENextOffSz);
  2062. if (op = OP_EXACTLY) or (op = OP_EXACTLYCI) then
  2063. regExactlyLen := PLongInt(regCode)
  2064. else
  2065. regExactlyLen := nil;
  2066. {$IFDEF DebugSynRegExpr}
  2067. if regcode - programm > regsize then
  2068. raise Exception.Create('TRegExpr.EmitNode buffer overrun');
  2069. {$ENDIF}
  2070. end
  2071. else
  2072. Inc(regCodeSize, REOpSz + RENextOffSz);
  2073. // compute code size without code generation
  2074. end; { of function TRegExpr.EmitNode
  2075. -------------------------------------------------------------- }
  2076. procedure TRegExpr.EmitC(ch: REChar);
  2077. begin
  2078. if regCode <> @regDummy then
  2079. begin
  2080. regCode^ := ch;
  2081. Inc(regCode);
  2082. {$IFDEF DebugSynRegExpr}
  2083. if regcode - programm > regsize then
  2084. raise Exception.Create('TRegExpr.EmitC buffer overrun');
  2085. {$ENDIF}
  2086. end
  2087. else
  2088. Inc(regCodeSize, REOpSz); // Type of p-code pointer always is ^REChar
  2089. end; { of procedure TRegExpr.EmitC
  2090. -------------------------------------------------------------- }
  2091. procedure TRegExpr.EmitInt(AValue: LongInt);
  2092. begin
  2093. if regCode <> @regDummy then
  2094. begin
  2095. PLongInt(regCode)^ := AValue;
  2096. Inc(regCode, RENumberSz);
  2097. {$IFDEF DebugSynRegExpr}
  2098. if regcode - programm > regsize then
  2099. raise Exception.Create('TRegExpr.EmitInt buffer overrun');
  2100. {$ENDIF}
  2101. end
  2102. else
  2103. Inc(regCodeSize, RENumberSz);
  2104. end;
  2105. function TRegExpr.EmitGroupRef(AIndex: integer; AIgnoreCase: boolean): PRegExprChar;
  2106. begin
  2107. if AIgnoreCase then
  2108. Result := EmitNode(OP_BSUBEXPCI)
  2109. else
  2110. Result := EmitNode(OP_BSUBEXP);
  2111. EmitC(REChar(AIndex));
  2112. end;
  2113. {$IFDEF FastUnicodeData}
  2114. procedure TRegExpr.FindCategoryName(var scan: PRegExprChar; var ch1, ch2: REChar);
  2115. // scan: points into regex string after '\p', to find category name
  2116. // ch1, ch2: 2-AnsiChar name of category; ch2 can be #0
  2117. var
  2118. ch: REChar;
  2119. pos1, pos2, namePtr: PRegExprChar;
  2120. nameLen: integer;
  2121. begin
  2122. ch1 := #0;
  2123. ch2 := #0;
  2124. ch := scan^;
  2125. if IsCategoryFirstChar(ch) then
  2126. begin
  2127. ch1 := ch;
  2128. Exit;
  2129. end;
  2130. if ch = '{' then
  2131. begin
  2132. pos1 := scan;
  2133. pos2 := pos1;
  2134. while (pos2 < fRegexEnd) and (pos2^ <> '}') do
  2135. Inc(pos2);
  2136. if pos2 >= fRegexEnd then
  2137. Error(reeIncorrectBraces);
  2138. namePtr := pos1+1;
  2139. nameLen := pos2-pos1-1;
  2140. Inc(scan, nameLen+1);
  2141. if nameLen<1 then
  2142. Error(reeBadUnicodeCategory);
  2143. if nameLen>2 then
  2144. Error(reeBadUnicodeCategory);
  2145. if nameLen = 1 then
  2146. begin
  2147. ch1 := namePtr^;
  2148. ch2 := #0;
  2149. if not IsCategoryFirstChar(ch1) then
  2150. Error(reeBadUnicodeCategory);
  2151. Exit;
  2152. end;
  2153. if nameLen = 2 then
  2154. begin
  2155. ch1 := namePtr^;
  2156. ch2 := (namePtr+1)^;
  2157. if not IsCategoryChars(ch1, ch2) then
  2158. Error(reeBadUnicodeCategory);
  2159. Exit;
  2160. end;
  2161. end
  2162. else
  2163. Error(reeBadUnicodeCategory);
  2164. end;
  2165. function TRegExpr.EmitCategoryMain(APositive: boolean): PRegExprChar;
  2166. var
  2167. ch, ch2: REChar;
  2168. begin
  2169. Inc(regParse);
  2170. if regParse >= fRegexEnd then
  2171. Error(reeBadUnicodeCategory);
  2172. FindCategoryName(regParse, ch, ch2);
  2173. if APositive then
  2174. Result := EmitNode(OP_ANYCATEGORY)
  2175. else
  2176. Result := EmitNode(OP_NOTCATEGORY);
  2177. EmitC(ch);
  2178. EmitC(ch2);
  2179. end;
  2180. {$ENDIF}
  2181. procedure TRegExpr.InsertOperator(op: TREOp; opnd: PRegExprChar; sz: integer);
  2182. // insert an operator in front of already-emitted operand
  2183. // Means relocating the operand.
  2184. var
  2185. src, dst, place: PRegExprChar;
  2186. i: integer;
  2187. begin
  2188. if regCode = @regDummy then
  2189. begin
  2190. Inc(regCodeSize, sz);
  2191. Exit;
  2192. end;
  2193. // move code behind insert position
  2194. src := regCode;
  2195. Inc(regCode, sz);
  2196. {$IFDEF DebugSynRegExpr}
  2197. if regCode - programm > regCodeSize then
  2198. raise Exception.Create('TRegExpr.InsertOperator buffer overrun');
  2199. // if (opnd<regCode) or (opnd-regCode>regCodeSize) then
  2200. // raise Exception.Create('TRegExpr.InsertOperator invalid opnd');
  2201. {$ENDIF}
  2202. dst := regCode;
  2203. while src > opnd do
  2204. begin
  2205. Dec(dst);
  2206. Dec(src);
  2207. dst^ := src^;
  2208. end;
  2209. place := opnd; // Op node, where operand used to be.
  2210. PREOp(place)^ := op;
  2211. Inc(place, REOpSz);
  2212. for i := 1 + REOpSz to sz do
  2213. begin
  2214. place^ := #0;
  2215. Inc(place);
  2216. end;
  2217. end; { of procedure TRegExpr.InsertOperator
  2218. -------------------------------------------------------------- }
  2219. function FindSkippedMetaLen(PStart, PEnd: PRegExprChar): integer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  2220. // find length of initial segment of PStart string consisting
  2221. // entirely of characters not from IsMetaSymbol1.
  2222. begin
  2223. Result := 0;
  2224. while PStart < PEnd do
  2225. begin
  2226. if _IsMetaSymbol1(PStart^) then
  2227. Exit;
  2228. Inc(Result);
  2229. Inc(PStart)
  2230. end;
  2231. end;
  2232. const
  2233. // Flags to be passed up and down.
  2234. FLAG_WORST = 0; // Worst case
  2235. FLAG_HASWIDTH = 1; // Cannot match empty string
  2236. FLAG_SIMPLE = 2; // Simple enough to be OP_STAR/OP_PLUS/OP_BRACES operand
  2237. FLAG_SPECSTART = 4; // Starts with * or +
  2238. {$IFDEF UnicodeRE}
  2239. RusRangeLoLow = #$430; // 'а'
  2240. RusRangeLoHigh = #$44F; // 'я'
  2241. RusRangeHiLow = #$410; // 'А'
  2242. RusRangeHiHigh = #$42F; // 'Я'
  2243. {$ELSE}
  2244. RusRangeLoLow = #$E0; // 'а' in cp1251
  2245. RusRangeLoHigh = #$FF; // 'я' in cp1251
  2246. RusRangeHiLow = #$C0; // 'А' in cp1251
  2247. RusRangeHiHigh = #$DF; // 'Я' in cp1251
  2248. {$ENDIF}
  2249. function TRegExpr.FindInCharClass(ABuffer: PRegExprChar; AChar: REChar; AIgnoreCase: boolean): boolean;
  2250. // Buffer contains AnsiChar pairs: (Kind, Data), where Kind is one of OpKind_ values,
  2251. // and Data depends on Kind
  2252. var
  2253. OpKind: REChar;
  2254. ch, ch2: REChar;
  2255. N, i: integer;
  2256. begin
  2257. if AIgnoreCase then
  2258. AChar := _UpperCase(AChar);
  2259. repeat
  2260. OpKind := ABuffer^;
  2261. case OpKind of
  2262. OpKind_End:
  2263. begin
  2264. Result := False;
  2265. Exit;
  2266. end;
  2267. OpKind_Range:
  2268. begin
  2269. Inc(ABuffer);
  2270. ch := ABuffer^;
  2271. Inc(ABuffer);
  2272. ch2 := ABuffer^;
  2273. Inc(ABuffer);
  2274. {
  2275. // if AIgnoreCase, ch, ch2 are upcased in opcode
  2276. if AIgnoreCase then
  2277. begin
  2278. ch := _UpperCase(ch);
  2279. ch2 := _UpperCase(ch2);
  2280. end;
  2281. }
  2282. if (AChar >= ch) and (AChar <= ch2) then
  2283. begin
  2284. Result := True;
  2285. Exit;
  2286. end;
  2287. end;
  2288. OpKind_MetaClass:
  2289. begin
  2290. Inc(ABuffer);
  2291. N := Ord(ABuffer^);
  2292. Inc(ABuffer);
  2293. if CharCheckers[N](AChar) then
  2294. begin
  2295. Result := True;
  2296. Exit
  2297. end;
  2298. end;
  2299. OpKind_Char:
  2300. begin
  2301. Inc(ABuffer);
  2302. N := PLongInt(ABuffer)^;
  2303. Inc(ABuffer, RENumberSz);
  2304. for i := 1 to N do
  2305. begin
  2306. ch := ABuffer^;
  2307. Inc(ABuffer);
  2308. {
  2309. // already upcased in opcode
  2310. if AIgnoreCase then
  2311. ch := _UpperCase(ch);
  2312. }
  2313. if ch = AChar then
  2314. begin
  2315. Result := True;
  2316. Exit;
  2317. end;
  2318. end;
  2319. end;
  2320. {$IFDEF FastUnicodeData}
  2321. OpKind_CategoryYes,
  2322. OpKind_CategoryNo:
  2323. begin
  2324. Inc(ABuffer);
  2325. ch := ABuffer^;
  2326. Inc(ABuffer);
  2327. ch2 := ABuffer^;
  2328. Inc(ABuffer);
  2329. Result := CheckCharCategory(AChar, ch, ch2);
  2330. if OpKind = OpKind_CategoryNo then
  2331. Result := not Result;
  2332. if Result then
  2333. Exit;
  2334. end;
  2335. {$ENDIF}
  2336. else
  2337. Error(reeBadOpcodeInCharClass);
  2338. end;
  2339. until False; // assume that Buffer is ended correctly
  2340. end;
  2341. procedure TRegExpr.GetCharSetFromWordChars(var ARes: TRegExprCharSet);
  2342. {$IFDEF UseWordChars}
  2343. var
  2344. i: integer;
  2345. ch: REChar;
  2346. {$ENDIF}
  2347. begin
  2348. {$IFDEF UseWordChars}
  2349. ARes := [];
  2350. for i := 1 to Length(fWordChars) do
  2351. begin
  2352. ch := fWordChars[i];
  2353. {$IFDEF UnicodeRE}
  2354. if Ord(ch) <= $FF then
  2355. {$ENDIF}
  2356. Include(ARes, byte(ch));
  2357. end;
  2358. {$ELSE}
  2359. ARes := RegExprWordSet;
  2360. {$ENDIF}
  2361. end;
  2362. procedure TRegExpr.GetCharSetFromSpaceChars(var ARes: TRegExprCharset);
  2363. {$IFDEF UseSpaceChars}
  2364. var
  2365. i: integer;
  2366. ch: REChar;
  2367. {$ENDIF}
  2368. begin
  2369. {$IFDEF UseSpaceChars}
  2370. ARes := [];
  2371. for i := 1 to Length(fSpaceChars) do
  2372. begin
  2373. ch := fSpaceChars[i];
  2374. {$IFDEF UnicodeRE}
  2375. if Ord(ch) <= $FF then
  2376. {$ENDIF}
  2377. Include(ARes, byte(ch));
  2378. end;
  2379. {$ELSE}
  2380. ARes := RegExprSpaceSet;
  2381. {$ENDIF}
  2382. end;
  2383. procedure TRegExpr.GetCharSetFromCharClass(ABuffer: PRegExprChar; AIgnoreCase: boolean; var ARes: TRegExprCharset);
  2384. var
  2385. ch, ch2: REChar;
  2386. TempSet: TRegExprCharSet;
  2387. N, i: integer;
  2388. begin
  2389. ARes := [];
  2390. TempSet := [];
  2391. repeat
  2392. case ABuffer^ of
  2393. OpKind_End:
  2394. Exit;
  2395. OpKind_Range:
  2396. begin
  2397. Inc(ABuffer);
  2398. ch := ABuffer^;
  2399. Inc(ABuffer);
  2400. ch2 := ABuffer^;
  2401. {$IFDEF UnicodeRE}
  2402. if Ord(ch2) > $FF then
  2403. ch2 := REChar($FF);
  2404. {$ENDIF}
  2405. Inc(ABuffer);
  2406. for i := Ord(ch) to Ord(ch2) do
  2407. begin
  2408. Include(ARes, byte(i));
  2409. if AIgnoreCase then
  2410. Include(ARes, byte(InvertCase(REChar(i))));
  2411. end;
  2412. end;
  2413. OpKind_MetaClass:
  2414. begin
  2415. Inc(ABuffer);
  2416. N := Ord(ABuffer^);
  2417. Inc(ABuffer);
  2418. if N = CheckerIndex_Word then
  2419. begin
  2420. GetCharSetFromWordChars(TempSet);
  2421. ARes := ARes + TempSet;
  2422. end
  2423. else
  2424. if N = CheckerIndex_NotWord then
  2425. begin
  2426. GetCharSetFromWordChars(TempSet);
  2427. ARes := ARes + (RegExprAllSet - TempSet);
  2428. end
  2429. else
  2430. if N = CheckerIndex_Space then
  2431. begin
  2432. GetCharSetFromSpaceChars(TempSet);
  2433. ARes := ARes + TempSet;
  2434. end
  2435. else
  2436. if N = CheckerIndex_NotSpace then
  2437. begin
  2438. GetCharSetFromSpaceChars(TempSet);
  2439. ARes := ARes + (RegExprAllSet - TempSet);
  2440. end
  2441. else
  2442. if N = CheckerIndex_Digit then
  2443. ARes := ARes + RegExprDigitSet
  2444. else
  2445. if N = CheckerIndex_NotDigit then
  2446. ARes := ARes + (RegExprAllSet - RegExprDigitSet)
  2447. else
  2448. if N = CheckerIndex_VertSep then
  2449. ARes := ARes + RegExprLineSeparatorsSet
  2450. else
  2451. if N = CheckerIndex_NotVertSep then
  2452. ARes := ARes + (RegExprAllSet - RegExprLineSeparatorsSet)
  2453. else
  2454. if N = CheckerIndex_HorzSep then
  2455. ARes := ARes + RegExprHorzSeparatorsSet
  2456. else
  2457. if N = CheckerIndex_NotHorzSep then
  2458. ARes := ARes + (RegExprAllSet - RegExprHorzSeparatorsSet)
  2459. else
  2460. if N = CheckerIndex_LowerAZ then
  2461. begin
  2462. if AIgnoreCase then
  2463. ARes := ARes + RegExprAllAzSet
  2464. else
  2465. ARes := ARes + RegExprLowerAzSet;
  2466. end
  2467. else
  2468. if N = CheckerIndex_UpperAZ then
  2469. begin
  2470. if AIgnoreCase then
  2471. ARes := ARes + RegExprAllAzSet
  2472. else
  2473. ARes := ARes + RegExprUpperAzSet;
  2474. end
  2475. else
  2476. Error(reeBadOpcodeInCharClass);
  2477. end;
  2478. OpKind_Char:
  2479. begin
  2480. Inc(ABuffer);
  2481. N := PLongInt(ABuffer)^;
  2482. Inc(ABuffer, RENumberSz);
  2483. for i := 1 to N do
  2484. begin
  2485. ch := ABuffer^;
  2486. Inc(ABuffer);
  2487. {$IFDEF UnicodeRE}
  2488. if Ord(ch) <= $FF then
  2489. {$ENDIF}
  2490. begin
  2491. Include(ARes, byte(ch));
  2492. if AIgnoreCase then
  2493. Include(ARes, byte(InvertCase(ch)));
  2494. end;
  2495. end;
  2496. end;
  2497. {$IFDEF FastUnicodeData}
  2498. OpKind_CategoryYes,
  2499. OpKind_CategoryNo:
  2500. begin
  2501. // usage of FirstCharSet makes no sense for regex with \p \P
  2502. ARes := RegExprAllSet;
  2503. Exit;
  2504. end;
  2505. {$ENDIF}
  2506. else
  2507. Error(reeBadOpcodeInCharClass);
  2508. end;
  2509. until False; // assume that Buffer is ended correctly
  2510. end;
  2511. function TRegExpr.GetModifierG: boolean;
  2512. begin
  2513. Result := fModifiers.G;
  2514. end;
  2515. function TRegExpr.GetModifierI: boolean;
  2516. begin
  2517. Result := fModifiers.I;
  2518. end;
  2519. function TRegExpr.GetModifierM: boolean;
  2520. begin
  2521. Result := fModifiers.M;
  2522. end;
  2523. function TRegExpr.GetModifierR: boolean;
  2524. begin
  2525. Result := fModifiers.R;
  2526. end;
  2527. function TRegExpr.GetModifierS: boolean;
  2528. begin
  2529. Result := fModifiers.S;
  2530. end;
  2531. function TRegExpr.GetModifierX: boolean;
  2532. begin
  2533. Result := fModifiers.X;
  2534. end;
  2535. function TRegExpr.CompileRegExpr(ARegExp: PRegExprChar): boolean;
  2536. // Compile a regular expression into internal code
  2537. // We can't allocate space until we know how big the compiled form will be,
  2538. // but we can't compile it (and thus know how big it is) until we've got a
  2539. // place to put the code. So we cheat: we compile it twice, once with code
  2540. // generation turned off and size counting turned on, and once "for real".
  2541. // This also means that we don't allocate space until we are sure that the
  2542. // thing really will compile successfully, and we never have to move the
  2543. // code and thus invalidate pointers into it. (Note that it has to be in
  2544. // one piece because free() must be able to free it all.)
  2545. // Beware that the optimization-preparation code in here knows about some
  2546. // of the structure of the compiled regexp.
  2547. var
  2548. scan, longest, longestTemp: PRegExprChar;
  2549. Len, LenTemp: integer;
  2550. FlagTemp: integer;
  2551. begin
  2552. Result := False;
  2553. FlagTemp := 0;
  2554. regParse := nil; // for correct error handling
  2555. regExactlyLen := nil;
  2556. ClearInternalIndexes;
  2557. fLastError := reeOk;
  2558. fLastErrorOpcode := TREOp(0);
  2559. if Assigned(fHelper) then
  2560. FreeAndNil(fHelper);
  2561. fHelperLen := 0;
  2562. try
  2563. if programm <> nil then
  2564. begin
  2565. FreeMem(programm);
  2566. programm := nil;
  2567. end;
  2568. if ARegExp = nil then
  2569. begin
  2570. Error(reeCompNullArgument);
  2571. Exit;
  2572. end;
  2573. fProgModifiers := fModifiers;
  2574. // well, may it's paranoia. I'll check it later.
  2575. // First pass: calculate opcode size, validate regex
  2576. fSecondPass := False;
  2577. fCompModifiers := fModifiers;
  2578. regParse := ARegExp;
  2579. regNumBrackets := 1;
  2580. regCodeSize := 0;
  2581. regCode := @regDummy;
  2582. regCodeWork := nil;
  2583. regLookahead := False;
  2584. regLookaheadNeg := False;
  2585. regLookaheadGroup := -1;
  2586. regLookbehind := False;
  2587. EmitC(OP_MAGIC);
  2588. if ParseReg(False, FlagTemp) = nil then
  2589. Exit;
  2590. // Allocate memory
  2591. GetMem(programm, regCodeSize * SizeOf(REChar));
  2592. // Second pass: emit opcode
  2593. fSecondPass := True;
  2594. fCompModifiers := fModifiers;
  2595. regParse := ARegExp;
  2596. regNumBrackets := 1;
  2597. regCode := programm;
  2598. regCodeWork := programm + REOpSz;
  2599. EmitC(OP_MAGIC);
  2600. if ParseReg(False, FlagTemp) = nil then
  2601. Exit;
  2602. // Dig out information for optimizations.
  2603. {$IFDEF UseFirstCharSet} // ###0.929
  2604. FirstCharSet := [];
  2605. FillFirstCharSet(regCodeWork);
  2606. for Len := 0 to 255 do
  2607. FirstCharArray[Len] := byte(Len) in FirstCharSet;
  2608. {$ENDIF}
  2609. regAnchored := #0;
  2610. regMust := nil;
  2611. regMustLen := 0;
  2612. regMustString := '';
  2613. scan := regCodeWork; // First OP_BRANCH.
  2614. if PREOp(regNext(scan))^ = OP_EEND then
  2615. begin // Only one top-level choice.
  2616. scan := scan + REOpSz + RENextOffSz;
  2617. // Starting-point info.
  2618. if PREOp(scan)^ = OP_BOL then
  2619. Inc(regAnchored);
  2620. // If there's something expensive in the r.e., find the longest
  2621. // literal string that must appear and make it the regMust. Resolve
  2622. // ties in favor of later strings, since the regstart check works
  2623. // with the beginning of the r.e. and avoiding duplication
  2624. // strengthens checking. Not a strong reason, but sufficient in the
  2625. // absence of others.
  2626. if (FlagTemp and FLAG_SPECSTART) <> 0 then
  2627. begin
  2628. longest := nil;
  2629. Len := 0;
  2630. while scan <> nil do
  2631. begin
  2632. if PREOp(scan)^ = OP_EXACTLY then
  2633. begin
  2634. longestTemp := scan + REOpSz + RENextOffSz + RENumberSz;
  2635. LenTemp := PLongInt(scan + REOpSz + RENextOffSz)^;
  2636. if LenTemp >= Len then
  2637. begin
  2638. longest := longestTemp;
  2639. Len := LenTemp;
  2640. end;
  2641. end;
  2642. scan := regNext(scan);
  2643. end;
  2644. regMust := longest;
  2645. regMustLen := Len;
  2646. if regMustLen > 1 then // don't use regMust if too short
  2647. SetString(regMustString, regMust, regMustLen);
  2648. end;
  2649. end;
  2650. Result := True;
  2651. finally
  2652. begin
  2653. if not Result then
  2654. InvalidateProgramm;
  2655. end;
  2656. end;
  2657. end; { of function TRegExpr.CompileRegExpr
  2658. -------------------------------------------------------------- }
  2659. function TRegExpr.ParseReg(InBrackets: boolean; var FlagParse: integer): PRegExprChar;
  2660. // regular expression, i.e. main body or parenthesized thing
  2661. // Caller must absorb opening parenthesis.
  2662. // Combining parenthesis handling with the base level of regular expression
  2663. // is a trifle forced, but the need to tie the tails of the branches to what
  2664. // follows makes it hard to avoid.
  2665. var
  2666. ret, br, ender: PRegExprChar;
  2667. NBrackets: integer;
  2668. FlagTemp: integer;
  2669. SavedModifiers: TRegExprModifiers;
  2670. begin
  2671. Result := nil;
  2672. FlagTemp := 0;
  2673. FlagParse := FLAG_HASWIDTH; // Tentatively.
  2674. NBrackets := 0;
  2675. SavedModifiers := fCompModifiers;
  2676. // Make an OP_OPEN node, if parenthesized.
  2677. if InBrackets then
  2678. begin
  2679. if regNumBrackets >= RegexMaxGroups then
  2680. begin
  2681. Error(reeCompParseRegTooManyBrackets);
  2682. Exit;
  2683. end;
  2684. NBrackets := regNumBrackets;
  2685. Inc(regNumBrackets);
  2686. ret := EmitNode(TREOp(Ord(OP_OPEN) + NBrackets));
  2687. GrpOpCodes[NBrackets] := ret;
  2688. end
  2689. else
  2690. ret := nil;
  2691. // Pick up the branches, linking them together.
  2692. br := ParseBranch(FlagTemp);
  2693. if br = nil then
  2694. begin
  2695. Result := nil;
  2696. Exit;
  2697. end;
  2698. if ret <> nil then
  2699. Tail(ret, br) // OP_OPEN -> first.
  2700. else
  2701. ret := br;
  2702. if (FlagTemp and FLAG_HASWIDTH) = 0 then
  2703. FlagParse := FlagParse and not FLAG_HASWIDTH;
  2704. FlagParse := FlagParse or FlagTemp and FLAG_SPECSTART;
  2705. while (regParse^ = '|') do
  2706. begin
  2707. Inc(regParse);
  2708. br := ParseBranch(FlagTemp);
  2709. if br = nil then
  2710. begin
  2711. Result := nil;
  2712. Exit;
  2713. end;
  2714. Tail(ret, br); // OP_BRANCH -> OP_BRANCH.
  2715. if (FlagTemp and FLAG_HASWIDTH) = 0 then
  2716. FlagParse := FlagParse and not FLAG_HASWIDTH;
  2717. FlagParse := FlagParse or FlagTemp and FLAG_SPECSTART;
  2718. end;
  2719. // Make a closing node, and hook it on the end.
  2720. if InBrackets then
  2721. ender := EmitNode(TREOp(Ord(OP_CLOSE) + NBrackets))
  2722. else
  2723. ender := EmitNode(OP_EEND);
  2724. Tail(ret, ender);
  2725. // Hook the tails of the branches to the closing node.
  2726. br := ret;
  2727. while br <> nil do
  2728. begin
  2729. OpTail(br, ender);
  2730. br := regNext(br);
  2731. end;
  2732. // Check for proper termination.
  2733. if InBrackets then
  2734. if regParse^ <> ')' then
  2735. begin
  2736. Error(reeCompParseRegUnmatchedBrackets);
  2737. Exit;
  2738. end
  2739. else
  2740. Inc(regParse); // skip trailing ')'
  2741. if (not InBrackets) and (regParse < fRegexEnd) then
  2742. begin
  2743. if regParse^ = ')' then
  2744. Error(reeCompParseRegUnmatchedBrackets2)
  2745. else
  2746. Error(reeCompParseRegJunkOnEnd);
  2747. Exit;
  2748. end;
  2749. fCompModifiers := SavedModifiers; // restore modifiers of parent
  2750. Result := ret;
  2751. end; { of function TRegExpr.ParseReg
  2752. -------------------------------------------------------------- }
  2753. function TRegExpr.ParseBranch(var FlagParse: integer): PRegExprChar;
  2754. // one alternative of an | operator
  2755. // Implements the concatenation operator.
  2756. var
  2757. ret, chain, latest: PRegExprChar;
  2758. FlagTemp: integer;
  2759. begin
  2760. FlagTemp := 0;
  2761. FlagParse := FLAG_WORST; // Tentatively.
  2762. ret := EmitNode(OP_BRANCH);
  2763. chain := nil;
  2764. while (regParse < fRegexEnd) and (regParse^ <> '|') and (regParse^ <> ')') do
  2765. begin
  2766. latest := ParsePiece(FlagTemp);
  2767. if latest = nil then
  2768. begin
  2769. Result := nil;
  2770. Exit;
  2771. end;
  2772. FlagParse := FlagParse or FlagTemp and FLAG_HASWIDTH;
  2773. if chain = nil // First piece.
  2774. then
  2775. FlagParse := FlagParse or FlagTemp and FLAG_SPECSTART
  2776. else
  2777. Tail(chain, latest);
  2778. chain := latest;
  2779. end;
  2780. if chain = nil // Loop ran zero times.
  2781. then
  2782. EmitNode(OP_NOTHING);
  2783. Result := ret;
  2784. end; { of function TRegExpr.ParseBranch
  2785. -------------------------------------------------------------- }
  2786. function TRegExpr.ParsePiece(var FlagParse: integer): PRegExprChar;
  2787. // something followed by possible [*+?{]
  2788. // Note that the branching code sequences used for ? and the general cases
  2789. // of * and + and { are somewhat optimized: they use the same OP_NOTHING node as
  2790. // both the endmarker for their branch list and the body of the last branch.
  2791. // It might seem that this node could be dispensed with entirely, but the
  2792. // endmarker role is not redundant.
  2793. function ParseNumber(AStart, AEnd: PRegExprChar): TREBracesArg;
  2794. begin
  2795. Result := 0;
  2796. if AEnd - AStart + 1 > 8 then
  2797. begin // prevent stupid scanning
  2798. Error(reeBRACESArgTooBig);
  2799. Exit;
  2800. end;
  2801. while AStart <= AEnd do
  2802. begin
  2803. Result := Result * 10 + (Ord(AStart^) - Ord('0'));
  2804. Inc(AStart);
  2805. end;
  2806. if (Result > MaxBracesArg) or (Result < 0) then
  2807. begin
  2808. Error(reeBRACESArgTooBig);
  2809. Exit;
  2810. end;
  2811. end;
  2812. var
  2813. TheOp: TREOp;
  2814. NextNode: PRegExprChar;
  2815. procedure EmitComplexBraces(ABracesMin, ABracesMax: TREBracesArg; ANonGreedyOp: boolean); // ###0.940
  2816. {$IFDEF ComplexBraces}
  2817. var
  2818. off: TRENextOff;
  2819. {$ENDIF}
  2820. begin
  2821. {$IFNDEF ComplexBraces}
  2822. Error(reeComplexBracesNotImplemented);
  2823. {$ELSE}
  2824. if ANonGreedyOp then
  2825. TheOp := OP_LOOPNG
  2826. else
  2827. TheOp := OP_LOOP;
  2828. InsertOperator(OP_LOOPENTRY, Result, REOpSz + RENextOffSz);
  2829. NextNode := EmitNode(TheOp);
  2830. if regCode <> @regDummy then
  2831. begin
  2832. off := (Result + REOpSz + RENextOffSz) - (regCode - REOpSz - RENextOffSz);
  2833. // back to Atom after OP_LOOPENTRY
  2834. PREBracesArg(AlignToInt(regCode))^ := ABracesMin;
  2835. Inc(regCode, REBracesArgSz);
  2836. PREBracesArg(AlignToInt(regCode))^ := ABracesMax;
  2837. Inc(regCode, REBracesArgSz);
  2838. PRENextOff(AlignToPtr(regCode))^ := off;
  2839. Inc(regCode, RENextOffSz);
  2840. {$IFDEF DebugSynRegExpr}
  2841. if regcode - programm > regsize then
  2842. raise Exception.Create
  2843. ('TRegExpr.ParsePiece.EmitComplexBraces buffer overrun');
  2844. {$ENDIF}
  2845. end
  2846. else
  2847. Inc(regCodeSize, REBracesArgSz * 2 + RENextOffSz);
  2848. Tail(Result, NextNode); // OP_LOOPENTRY -> OP_LOOP
  2849. if regCode <> @regDummy then
  2850. Tail(Result + REOpSz + RENextOffSz, NextNode); // Atom -> OP_LOOP
  2851. {$ENDIF}
  2852. end;
  2853. procedure EmitSimpleBraces(ABracesMin, ABracesMax: TREBracesArg; ANonGreedyOp, APossessive: boolean);
  2854. begin
  2855. if APossessive then
  2856. TheOp := OP_BRACES_POSS
  2857. else
  2858. if ANonGreedyOp then
  2859. TheOp := OP_BRACESNG
  2860. else
  2861. TheOp := OP_BRACES;
  2862. InsertOperator(TheOp, Result, REOpSz + RENextOffSz + REBracesArgSz * 2);
  2863. if regCode <> @regDummy then
  2864. begin
  2865. PREBracesArg(AlignToInt(Result + REOpSz + RENextOffSz))^ := ABracesMin;
  2866. PREBracesArg(AlignToInt(Result + REOpSz + RENextOffSz + REBracesArgSz))^ := ABracesMax;
  2867. end;
  2868. end;
  2869. var
  2870. op, nextch: REChar;
  2871. NonGreedyOp, NonGreedyCh, PossessiveCh: boolean;
  2872. FlagTemp: integer;
  2873. BracesMin, BracesMax: TREBracesArg;
  2874. p: PRegExprChar;
  2875. begin
  2876. FlagTemp := 0;
  2877. Result := ParseAtom(FlagTemp);
  2878. if Result = nil then
  2879. Exit;
  2880. op := regParse^;
  2881. if not ((op = '*') or (op = '+') or (op = '?') or (op = '{')) then
  2882. begin
  2883. FlagParse := FlagTemp;
  2884. Exit;
  2885. end;
  2886. if ((FlagTemp and FLAG_HASWIDTH) = 0) and (op <> '?') then
  2887. begin
  2888. Error(reePlusStarOperandCouldBeEmpty);
  2889. Exit;
  2890. end;
  2891. case op of
  2892. '*':
  2893. begin
  2894. FlagParse := FLAG_WORST or FLAG_SPECSTART;
  2895. nextch := (regParse + 1)^;
  2896. PossessiveCh := nextch = '+';
  2897. if PossessiveCh then
  2898. begin
  2899. NonGreedyCh := False;
  2900. NonGreedyOp := False;
  2901. end
  2902. else
  2903. begin
  2904. NonGreedyCh := nextch = '?';
  2905. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2906. end;
  2907. if (FlagTemp and FLAG_SIMPLE) = 0 then
  2908. begin
  2909. if NonGreedyOp then
  2910. EmitComplexBraces(0, MaxBracesArg, NonGreedyOp)
  2911. else
  2912. begin // Emit x* as (x&|), where & means "self".
  2913. InsertOperator(OP_BRANCH, Result, REOpSz + RENextOffSz); // Either x
  2914. OpTail(Result, EmitNode(OP_BACK)); // and loop
  2915. OpTail(Result, Result); // back
  2916. Tail(Result, EmitNode(OP_BRANCH)); // or
  2917. Tail(Result, EmitNode(OP_NOTHING)); // nil.
  2918. end
  2919. end
  2920. else
  2921. begin // Simple
  2922. if PossessiveCh then
  2923. TheOp := OP_STAR_POSS
  2924. else
  2925. if NonGreedyOp then
  2926. TheOp := OP_STARNG
  2927. else
  2928. TheOp := OP_STAR;
  2929. InsertOperator(TheOp, Result, REOpSz + RENextOffSz);
  2930. end;
  2931. if NonGreedyCh or PossessiveCh then
  2932. Inc(regParse); // Skip extra AnsiChar ('?')
  2933. end; { of case '*' }
  2934. '+':
  2935. begin
  2936. FlagParse := FLAG_WORST or FLAG_SPECSTART or FLAG_HASWIDTH;
  2937. nextch := (regParse + 1)^;
  2938. PossessiveCh := nextch = '+';
  2939. if PossessiveCh then
  2940. begin
  2941. NonGreedyCh := False;
  2942. NonGreedyOp := False;
  2943. end
  2944. else
  2945. begin
  2946. NonGreedyCh := nextch = '?';
  2947. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2948. end;
  2949. if (FlagTemp and FLAG_SIMPLE) = 0 then
  2950. begin
  2951. if NonGreedyOp then
  2952. EmitComplexBraces(1, MaxBracesArg, NonGreedyOp)
  2953. else
  2954. begin // Emit x+ as x(&|), where & means "self".
  2955. NextNode := EmitNode(OP_BRANCH); // Either
  2956. Tail(Result, NextNode);
  2957. Tail(EmitNode(OP_BACK), Result); // loop back
  2958. Tail(NextNode, EmitNode(OP_BRANCH)); // or
  2959. Tail(Result, EmitNode(OP_NOTHING)); // nil.
  2960. end
  2961. end
  2962. else
  2963. begin // Simple
  2964. if PossessiveCh then
  2965. TheOp := OP_PLUS_POSS
  2966. else
  2967. if NonGreedyOp then
  2968. TheOp := OP_PLUSNG
  2969. else
  2970. TheOp := OP_PLUS;
  2971. InsertOperator(TheOp, Result, REOpSz + RENextOffSz);
  2972. end;
  2973. if NonGreedyCh or PossessiveCh then
  2974. Inc(regParse); // Skip extra AnsiChar ('?')
  2975. end; { of case '+' }
  2976. '?':
  2977. begin
  2978. FlagParse := FLAG_WORST;
  2979. nextch := (regParse + 1)^;
  2980. PossessiveCh := nextch = '+';
  2981. if PossessiveCh then
  2982. begin
  2983. NonGreedyCh := False;
  2984. NonGreedyOp := False;
  2985. end
  2986. else
  2987. begin
  2988. NonGreedyCh := nextch = '?';
  2989. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2990. end;
  2991. if NonGreedyOp or PossessiveCh then
  2992. begin // ###0.940 // We emit x?? as x{0,1}?
  2993. if (FlagTemp and FLAG_SIMPLE) = 0 then
  2994. begin
  2995. if PossessiveCh then
  2996. Error(reePossessiveAfterComplexBraces);
  2997. EmitComplexBraces(0, 1, NonGreedyOp);
  2998. end
  2999. else
  3000. EmitSimpleBraces(0, 1, NonGreedyOp, PossessiveCh);
  3001. end
  3002. else
  3003. begin // greedy '?'
  3004. InsertOperator(OP_BRANCH, Result, REOpSz + RENextOffSz); // Either x
  3005. Tail(Result, EmitNode(OP_BRANCH)); // or
  3006. NextNode := EmitNode(OP_NOTHING); // nil.
  3007. Tail(Result, NextNode);
  3008. OpTail(Result, NextNode);
  3009. end;
  3010. if NonGreedyCh or PossessiveCh then
  3011. Inc(regParse); // Skip extra AnsiChar ('?')
  3012. end; { of case '?' }
  3013. '{':
  3014. begin
  3015. Inc(regParse);
  3016. p := regParse;
  3017. while IsDigitChar(regParse^) do // <min> MUST appear
  3018. Inc(regParse);
  3019. if (regParse^ <> '}') and (regParse^ <> ',') or (p = regParse) then
  3020. begin
  3021. Error(reeIncorrectBraces);
  3022. Exit;
  3023. end;
  3024. BracesMin := ParseNumber(p, regParse - 1);
  3025. if regParse^ = ',' then
  3026. begin
  3027. Inc(regParse);
  3028. p := regParse;
  3029. while IsDigitChar(regParse^) do
  3030. Inc(regParse);
  3031. if regParse^ <> '}' then
  3032. begin
  3033. Error(reeIncorrectBraces);
  3034. Exit;
  3035. end;
  3036. if p = regParse then
  3037. BracesMax := MaxBracesArg
  3038. else
  3039. BracesMax := ParseNumber(p, regParse - 1);
  3040. end
  3041. else
  3042. BracesMax := BracesMin; // {n} == {n,n}
  3043. if BracesMin > BracesMax then
  3044. begin
  3045. Error(reeBracesMinParamGreaterMax);
  3046. Exit;
  3047. end;
  3048. if BracesMin > 0 then
  3049. FlagParse := FLAG_WORST;
  3050. if BracesMax > 0 then
  3051. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SPECSTART;
  3052. nextch := (regParse + 1)^;
  3053. PossessiveCh := nextch = '+';
  3054. if PossessiveCh then
  3055. begin
  3056. NonGreedyCh := False;
  3057. NonGreedyOp := False;
  3058. end
  3059. else
  3060. begin
  3061. NonGreedyCh := nextch = '?';
  3062. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  3063. end;
  3064. if (FlagTemp and FLAG_SIMPLE) <> 0 then
  3065. EmitSimpleBraces(BracesMin, BracesMax, NonGreedyOp, PossessiveCh)
  3066. else
  3067. begin
  3068. if PossessiveCh then
  3069. Error(reePossessiveAfterComplexBraces);
  3070. EmitComplexBraces(BracesMin, BracesMax, NonGreedyOp);
  3071. end;
  3072. if NonGreedyCh or PossessiveCh then
  3073. Inc(regParse); // Skip extra AnsiChar '?'
  3074. end; // of case '{'
  3075. // else // here we can't be
  3076. end; { of case op }
  3077. Inc(regParse);
  3078. op := regParse^;
  3079. if (op = '*') or (op = '+') or (op = '?') or (op = '{') then
  3080. Error(reeNestedQuantif);
  3081. end; { of function TRegExpr.ParsePiece
  3082. -------------------------------------------------------------- }
  3083. function TRegExpr.HexDig(Ch: REChar): integer;
  3084. begin
  3085. case Ch of
  3086. '0' .. '9':
  3087. Result := Ord(Ch) - Ord('0');
  3088. 'a' .. 'f':
  3089. Result := Ord(Ch) - Ord('a') + 10;
  3090. 'A' .. 'F':
  3091. Result := Ord(Ch) - Ord('A') + 10;
  3092. else
  3093. begin
  3094. Result := 0;
  3095. Error(reeBadHexDigit);
  3096. end;
  3097. end;
  3098. end;
  3099. function TRegExpr.UnQuoteChar(var APtr, AEnd: PRegExprChar): REChar;
  3100. var
  3101. Ch: REChar;
  3102. begin
  3103. case APtr^ of
  3104. 't':
  3105. Result := #$9; // \t => tab (HT/TAB)
  3106. 'n':
  3107. Result := #$a; // \n => newline (NL)
  3108. 'r':
  3109. Result := #$d; // \r => carriage return (CR)
  3110. 'f':
  3111. Result := #$c; // \f => form feed (FF)
  3112. 'a':
  3113. Result := #$7; // \a => alarm (bell) (BEL)
  3114. 'e':
  3115. Result := #$1b; // \e => escape (ESC)
  3116. 'c':
  3117. begin // \cK => code for Ctrl+K
  3118. Result := #0;
  3119. Inc(APtr);
  3120. if APtr >= AEnd then
  3121. Error(reeNoLetterAfterBSlashC);
  3122. Ch := APtr^;
  3123. case Ch of
  3124. 'a' .. 'z':
  3125. Result := REChar(Ord(Ch) - Ord('a') + 1);
  3126. 'A' .. 'Z':
  3127. Result := REChar(Ord(Ch) - Ord('A') + 1);
  3128. else
  3129. Error(reeNoLetterAfterBSlashC);
  3130. end;
  3131. end;
  3132. 'x':
  3133. begin // \x: hex AnsiChar
  3134. Result := #0;
  3135. Inc(APtr);
  3136. if APtr >= AEnd then
  3137. begin
  3138. Error(reeNoHexCodeAfterBSlashX);
  3139. Exit;
  3140. end;
  3141. if APtr^ = '{' then
  3142. begin // \x{nnnn} //###0.936
  3143. repeat
  3144. Inc(APtr);
  3145. if APtr >= AEnd then
  3146. begin
  3147. Error(reeNoHexCodeAfterBSlashX);
  3148. Exit;
  3149. end;
  3150. if APtr^ <> '}' then
  3151. begin
  3152. if (Ord(Result) ShR (SizeOf(REChar) * 8 - 4)) and $F <> 0 then
  3153. begin
  3154. Error(reeHexCodeAfterBSlashXTooBig);
  3155. Exit;
  3156. end;
  3157. Result := REChar((Ord(Result) ShL 4) or HexDig(APtr^));
  3158. // HexDig will cause Error if bad hex digit found
  3159. end
  3160. else
  3161. Break;
  3162. until False;
  3163. end
  3164. else
  3165. begin
  3166. Result := REChar(HexDig(APtr^));
  3167. // HexDig will cause Error if bad hex digit found
  3168. Inc(APtr);
  3169. if APtr >= AEnd then
  3170. begin
  3171. Error(reeNoHexCodeAfterBSlashX);
  3172. Exit;
  3173. end;
  3174. Result := REChar((Ord(Result) ShL 4) or HexDig(APtr^));
  3175. // HexDig will cause Error if bad hex digit found
  3176. end;
  3177. end;
  3178. else
  3179. begin
  3180. Result := APtr^;
  3181. if (Result <> '_') and IsWordChar(Result) then
  3182. begin
  3183. fLastErrorSymbol := Result;
  3184. Error(reeUnknownMetaSymbol);
  3185. end;
  3186. end;
  3187. end;
  3188. end;
  3189. function TRegExpr.ParseAtom(var FlagParse: integer): PRegExprChar;
  3190. // the lowest level
  3191. // Optimization: gobbles an entire sequence of ordinary characters so that
  3192. // it can turn them into a single node, which is smaller to store and
  3193. // faster to run. Backslashed characters are exceptions, each becoming a
  3194. // separate node; the code is simpler that way and it's not worth fixing.
  3195. var
  3196. ret: PRegExprChar;
  3197. RangeBeg, RangeEnd: REChar;
  3198. CanBeRange: boolean;
  3199. AddrOfLen: PLongInt;
  3200. procedure EmitExactly(Ch: REChar);
  3201. begin
  3202. if fCompModifiers.I then
  3203. ret := EmitNode(OP_EXACTLYCI)
  3204. else
  3205. ret := EmitNode(OP_EXACTLY);
  3206. EmitInt(1);
  3207. EmitC(Ch);
  3208. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3209. end;
  3210. procedure EmitRangeChar(Ch: REChar; AStartOfRange: boolean);
  3211. begin
  3212. CanBeRange := AStartOfRange;
  3213. if fCompModifiers.I then
  3214. Ch := _UpperCase(Ch);
  3215. if AStartOfRange then
  3216. begin
  3217. AddrOfLen := nil;
  3218. RangeBeg := Ch;
  3219. end
  3220. else
  3221. begin
  3222. if AddrOfLen = nil then
  3223. begin
  3224. EmitC(OpKind_Char);
  3225. Pointer(AddrOfLen) := regCode;
  3226. EmitInt(0);
  3227. end;
  3228. Inc(AddrOfLen^);
  3229. EmitC(Ch);
  3230. end;
  3231. end;
  3232. procedure EmitRangePacked(ch1, ch2: REChar);
  3233. var
  3234. ChkIndex: integer;
  3235. begin
  3236. AddrOfLen := nil;
  3237. CanBeRange := False;
  3238. if fCompModifiers.I then
  3239. begin
  3240. ch1 := _UpperCase(ch1);
  3241. ch2 := _UpperCase(ch2);
  3242. end;
  3243. for ChkIndex := Low(CharCheckerInfos) to High(CharCheckerInfos) do
  3244. if (CharCheckerInfos[ChkIndex].CharBegin = ch1) and
  3245. (CharCheckerInfos[ChkIndex].CharEnd = ch2) then
  3246. begin
  3247. EmitC(OpKind_MetaClass);
  3248. EmitC(REChar(CharCheckerInfos[ChkIndex].CheckerIndex));
  3249. Exit;
  3250. end;
  3251. EmitC(OpKind_Range);
  3252. EmitC(ch1);
  3253. EmitC(ch2);
  3254. end;
  3255. {$IFDEF FastUnicodeData}
  3256. procedure EmitCategoryInCharClass(APositive: boolean);
  3257. var
  3258. ch, ch2: REChar;
  3259. begin
  3260. AddrOfLen := nil;
  3261. CanBeRange := False;
  3262. Inc(regParse);
  3263. FindCategoryName(regParse, ch, ch2);
  3264. if APositive then
  3265. EmitC(OpKind_CategoryYes)
  3266. else
  3267. EmitC(OpKind_CategoryNo);
  3268. EmitC(ch);
  3269. EmitC(ch2);
  3270. end;
  3271. {$ENDIF}
  3272. var
  3273. FlagTemp: integer;
  3274. Len: integer;
  3275. SavedPtr: PRegExprChar;
  3276. EnderChar, TempChar: REChar;
  3277. DashForRange: Boolean;
  3278. GrpKind: TREGroupKind;
  3279. GrpName: RegExprString;
  3280. GrpIndex: integer;
  3281. NextCh: REChar;
  3282. begin
  3283. Result := nil;
  3284. FlagTemp := 0;
  3285. FlagParse := FLAG_WORST;
  3286. AddrOfLen := nil;
  3287. GrpIndex := -1;
  3288. Inc(regParse);
  3289. case (regParse - 1)^ of
  3290. '^':
  3291. begin
  3292. if not fCompModifiers.M
  3293. {$IFDEF UseLineSep} or (fLineSeparators = '') {$ENDIF} then
  3294. ret := EmitNode(OP_BOL)
  3295. else
  3296. ret := EmitNode(OP_BOLML);
  3297. end;
  3298. '$':
  3299. begin
  3300. if not fCompModifiers.M
  3301. {$IFDEF UseLineSep} or (fLineSeparators = '') {$ENDIF} then
  3302. ret := EmitNode(OP_EOL)
  3303. else
  3304. ret := EmitNode(OP_EOLML);
  3305. end;
  3306. '.':
  3307. begin
  3308. if fCompModifiers.S then
  3309. begin
  3310. ret := EmitNode(OP_ANY);
  3311. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3312. end
  3313. else
  3314. begin // not /s, so emit [^:LineSeparators:]
  3315. ret := EmitNode(OP_ANYML);
  3316. FlagParse := FlagParse or FLAG_HASWIDTH; // not so simple ;)
  3317. end;
  3318. end;
  3319. '[':
  3320. begin
  3321. if regParse^ = '^' then
  3322. begin // Complement of range.
  3323. if fCompModifiers.I then
  3324. ret := EmitNode(OP_ANYBUTCI)
  3325. else
  3326. ret := EmitNode(OP_ANYBUT);
  3327. Inc(regParse);
  3328. end
  3329. else if fCompModifiers.I then
  3330. ret := EmitNode(OP_ANYOFCI)
  3331. else
  3332. ret := EmitNode(OP_ANYOF);
  3333. CanBeRange := False;
  3334. if regParse^ = ']' then
  3335. begin
  3336. // first ']' inside [] treated as simple AnsiChar, no need to check '['
  3337. EmitRangeChar(regParse^, (regParse + 1)^ = '-');
  3338. Inc(regParse);
  3339. end;
  3340. while (regParse < fRegexEnd) and (regParse^ <> ']') do
  3341. begin
  3342. // last '-' inside [] treated as simple dash
  3343. if (regParse^ = '-') and
  3344. ((regParse + 1) < fRegexEnd) and
  3345. ((regParse + 1)^ = ']') then
  3346. begin
  3347. EmitRangeChar('-', False);
  3348. Inc(regParse);
  3349. Break;
  3350. end;
  3351. // AnsiChar '-' which (maybe) makes a range
  3352. if (regParse^ = '-') and ((regParse + 1) < fRegexEnd) and CanBeRange then
  3353. begin
  3354. Inc(regParse);
  3355. RangeEnd := regParse^;
  3356. if RangeEnd = EscChar then
  3357. begin
  3358. if _IsMetaChar((regParse + 1)^) then
  3359. begin
  3360. Error(reeMetaCharAfterMinusInRange);
  3361. Exit;
  3362. end;
  3363. Inc(regParse);
  3364. RangeEnd := UnQuoteChar(regParse, fRegexEnd);
  3365. end;
  3366. // special handling for Russian range a-YA, add 2 ranges: a-ya and A-YA
  3367. if fCompModifiers.R and
  3368. (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeHiHigh) then
  3369. begin
  3370. EmitRangePacked(RusRangeLoLow, RusRangeLoHigh);
  3371. EmitRangePacked(RusRangeHiLow, RusRangeHiHigh);
  3372. end
  3373. else
  3374. begin // standard r.e. handling
  3375. if RangeBeg > RangeEnd then
  3376. begin
  3377. Error(reeInvalidRange);
  3378. Exit;
  3379. end;
  3380. EmitRangePacked(RangeBeg, RangeEnd);
  3381. end;
  3382. Inc(regParse);
  3383. end
  3384. else
  3385. begin
  3386. if regParse^ = EscChar then
  3387. begin
  3388. Inc(regParse);
  3389. if regParse >= fRegexEnd then
  3390. begin
  3391. Error(reeParseAtomTrailingBackSlash);
  3392. Exit;
  3393. end;
  3394. if _IsMetaChar(regParse^) then
  3395. begin
  3396. AddrOfLen := nil;
  3397. CanBeRange := False;
  3398. EmitC(OpKind_MetaClass);
  3399. case regParse^ of
  3400. 'w':
  3401. EmitC(REChar(CheckerIndex_Word));
  3402. 'W':
  3403. EmitC(REChar(CheckerIndex_NotWord));
  3404. 's':
  3405. EmitC(REChar(CheckerIndex_Space));
  3406. 'S':
  3407. EmitC(REChar(CheckerIndex_NotSpace));
  3408. 'd':
  3409. EmitC(REChar(CheckerIndex_Digit));
  3410. 'D':
  3411. EmitC(REChar(CheckerIndex_NotDigit));
  3412. 'v':
  3413. EmitC(REChar(CheckerIndex_VertSep));
  3414. 'V':
  3415. EmitC(REChar(CheckerIndex_NotVertSep));
  3416. 'h':
  3417. EmitC(REChar(CheckerIndex_HorzSep));
  3418. 'H':
  3419. EmitC(REChar(CheckerIndex_NotHorzSep));
  3420. else
  3421. Error(reeBadOpcodeInCharClass);
  3422. end;
  3423. end
  3424. else
  3425. {$IFDEF FastUnicodeData}
  3426. if regParse^ = 'p' then
  3427. EmitCategoryInCharClass(True)
  3428. else
  3429. if regParse^ = 'P' then
  3430. EmitCategoryInCharClass(False)
  3431. else
  3432. {$ENDIF}
  3433. begin
  3434. TempChar := UnQuoteChar(regParse, fRegexEnd);
  3435. // False if '-' is last AnsiChar in []
  3436. DashForRange :=
  3437. (regParse + 2 < fRegexEnd) and
  3438. ((regParse + 1)^ = '-') and
  3439. ((regParse + 2)^ <> ']');
  3440. EmitRangeChar(TempChar, DashForRange);
  3441. end;
  3442. end
  3443. else
  3444. begin
  3445. // False if '-' is last AnsiChar in []
  3446. DashForRange :=
  3447. (regParse + 2 < fRegexEnd) and
  3448. ((regParse + 1)^ = '-') and
  3449. ((regParse + 2)^ <> ']');
  3450. EmitRangeChar(regParse^, DashForRange);
  3451. end;
  3452. Inc(regParse);
  3453. end;
  3454. end; { of while }
  3455. AddrOfLen := nil;
  3456. CanBeRange := False;
  3457. EmitC(OpKind_End);
  3458. if regParse^ <> ']' then
  3459. begin
  3460. Error(reeUnmatchedSqBrackets);
  3461. Exit;
  3462. end;
  3463. Inc(regParse);
  3464. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3465. end;
  3466. '(':
  3467. begin
  3468. GrpKind := gkNormalGroup;
  3469. GrpName := '';
  3470. // A: detect kind of expression in brackets
  3471. if regParse^ = '?' then
  3472. begin
  3473. NextCh := (regParse + 1)^;
  3474. case NextCh of
  3475. ':':
  3476. begin
  3477. // non-capturing group: (?:regex)
  3478. GrpKind := gkNonCapturingGroup;
  3479. Inc(regParse, 2);
  3480. end;
  3481. '>':
  3482. begin
  3483. // atomic group: (?>regex)
  3484. GrpKind := gkNonCapturingGroup;
  3485. Inc(regParse, 2);
  3486. GrpAtomic[regNumBrackets] := True;
  3487. end;
  3488. 'P':
  3489. begin
  3490. if (regParse + 4 >= fRegexEnd) then
  3491. Error(reeNamedGroupBad);
  3492. case (regParse + 2)^ of
  3493. '<':
  3494. begin
  3495. // named group: (?P<name>regex)
  3496. GrpKind := gkNormalGroup;
  3497. FindGroupName(regParse + 3, fRegexEnd, '>', GrpName);
  3498. Inc(regParse, Length(GrpName) + 4);
  3499. end;
  3500. '=':
  3501. begin
  3502. // back-reference to named group: (?P=name)
  3503. GrpKind := gkNamedGroupReference;
  3504. FindGroupName(regParse + 3, fRegexEnd, ')', GrpName);
  3505. Inc(regParse, Length(GrpName) + 4);
  3506. end;
  3507. '>':
  3508. begin
  3509. // subroutine call to named group: (?P>name)
  3510. GrpKind := gkSubCall;
  3511. FindGroupName(regParse + 3, fRegexEnd, ')', GrpName);
  3512. Inc(regParse, Length(GrpName) + 4);
  3513. GrpIndex := MatchIndexFromName(GrpName);
  3514. if GrpIndex < 1 then
  3515. Error(reeNamedGroupBadRef);
  3516. end;
  3517. else
  3518. Error(reeNamedGroupBad);
  3519. end;
  3520. end;
  3521. '<':
  3522. begin
  3523. // lookbehind: (?<=foo)bar
  3524. if (regParse + 4 >= fRegexEnd) then
  3525. Error(reeLookbehindBad);
  3526. case (regParse + 2)^ of
  3527. '=':
  3528. begin
  3529. // allow lookbehind only at the beginning
  3530. if regParse <> fRegexStart + 1 then
  3531. Error(reeLookaroundNotAtEdge);
  3532. GrpKind := gkLookbehind;
  3533. GrpAtomic[regNumBrackets] := RegExprLookbehindIsAtomic;
  3534. regLookbehind := True;
  3535. Inc(regParse, 3);
  3536. end;
  3537. '!':
  3538. begin
  3539. // allow lookbehind only at the beginning
  3540. if regParse <> fRegexStart + 1 then
  3541. Error(reeLookaroundNotAtEdge);
  3542. GrpKind := gkLookbehindNeg;
  3543. Inc(regParse, 3);
  3544. SavedPtr := _FindClosingBracket(regParse, fRegexEnd);
  3545. if SavedPtr = nil then
  3546. Error(reeCompParseRegUnmatchedBrackets);
  3547. // for '(?<!foo)bar', we make our regex 'bar' and make Helper object with 'foo'
  3548. if not fSecondPass then
  3549. begin
  3550. Len := SavedPtr - fRegexStart - 4;
  3551. if Len = 0 then
  3552. Error(reeLookbehindBad);
  3553. if fHelper = nil then
  3554. fHelper := TRegExpr.Create;
  3555. fHelper.Expression := Copy(fExpression, 5, Len);
  3556. try
  3557. fHelper.Compile;
  3558. except
  3559. Len := fHelper.LastError;
  3560. FreeAndNil(fHelper);
  3561. Error(Len);
  3562. end;
  3563. if fHelper.IsFixedLength(TempChar, Len) then
  3564. fHelperLen := Len
  3565. else
  3566. begin
  3567. FreeAndNil(fHelper);
  3568. Error(reeLookbehindTooComplex);
  3569. end;
  3570. end;
  3571. // jump to closing bracket, don't make opcode for (?<!foo)
  3572. regParse := SavedPtr + 1;
  3573. end;
  3574. else
  3575. Error(reeLookbehindBad);
  3576. end;
  3577. end;
  3578. '=', '!':
  3579. begin
  3580. // lookaheads: foo(?=bar) and foo(?!bar)
  3581. if (regParse + 3 >= fRegexEnd) then
  3582. Error(reeLookaheadBad);
  3583. regLookahead := True;
  3584. regLookaheadGroup := regNumBrackets;
  3585. if NextCh = '=' then
  3586. begin
  3587. GrpKind := gkLookahead;
  3588. end
  3589. else
  3590. begin
  3591. GrpKind := gkLookaheadNeg;
  3592. regLookaheadNeg := True;
  3593. end;
  3594. GrpAtomic[regNumBrackets] := RegExprLookaheadIsAtomic;
  3595. // check that these brackets are last in regex
  3596. SavedPtr := _FindClosingBracket(regParse + 1, fRegexEnd);
  3597. if (SavedPtr <> fRegexEnd - 1) then
  3598. Error(reeLookaroundNotAtEdge);
  3599. Inc(regParse, 2);
  3600. end;
  3601. '#':
  3602. begin
  3603. // (?#comment)
  3604. GrpKind := gkComment;
  3605. Inc(regParse, 2);
  3606. end;
  3607. 'a'..'z', '-':
  3608. begin
  3609. // modifiers string like (?mxr)
  3610. GrpKind := gkModifierString;
  3611. Inc(regParse);
  3612. end;
  3613. 'R', '0':
  3614. begin
  3615. // recursion (?R), (?0)
  3616. GrpKind := gkRecursion;
  3617. Inc(regParse, 2);
  3618. if regParse^ <> ')' then
  3619. Error(reeBadRecursion);
  3620. Inc(regParse);
  3621. end;
  3622. '1'..'9':
  3623. begin
  3624. // subroutine call (?1)..(?99)
  3625. GrpKind := gkSubCall;
  3626. GrpIndex := Ord(NextCh) - Ord('0');
  3627. Inc(regParse, 2);
  3628. // support 2-digit group numbers
  3629. case regParse^ of
  3630. ')':
  3631. begin
  3632. Inc(regParse);
  3633. end;
  3634. '0'..'9':
  3635. begin
  3636. GrpIndex := GrpIndex * 10 + Ord(regParse^) - Ord('0');
  3637. if GrpIndex >= RegexMaxGroups then
  3638. Error(reeBadSubCall);
  3639. Inc(regParse);
  3640. if regParse^ <> ')' then
  3641. Error(reeBadSubCall);
  3642. Inc(regParse);
  3643. end
  3644. else
  3645. Error(reeBadRecursion);
  3646. end;
  3647. end;
  3648. '''':
  3649. begin
  3650. // named group: (?'name'regex)
  3651. if (regParse + 4 >= fRegexEnd) then
  3652. Error(reeNamedGroupBad);
  3653. GrpKind := gkNormalGroup;
  3654. FindGroupName(regParse + 2, fRegexEnd, '''', GrpName);
  3655. Inc(regParse, Length(GrpName) + 3);
  3656. end;
  3657. '&':
  3658. begin
  3659. // subroutine call to named group: (?&name)
  3660. if (regParse + 2 >= fRegexEnd) then
  3661. Error(reeBadSubCall);
  3662. GrpKind := gkSubCall;
  3663. FindGroupName(regParse + 2, fRegexEnd, ')', GrpName);
  3664. Inc(regParse, Length(GrpName) + 3);
  3665. GrpIndex := MatchIndexFromName(GrpName);
  3666. if GrpIndex < 1 then
  3667. Error(reeNamedGroupBadRef);
  3668. end;
  3669. else
  3670. Error(reeIncorrectSpecialBrackets);
  3671. end;
  3672. end;
  3673. // B: process found kind of brackets
  3674. case GrpKind of
  3675. gkNormalGroup,
  3676. gkNonCapturingGroup,
  3677. gkLookahead,
  3678. gkLookaheadNeg,
  3679. gkLookbehind:
  3680. begin
  3681. // skip this block for one of passes, to not double groups count;
  3682. // must take first pass (we need GrpNames filled)
  3683. if (GrpKind = gkNormalGroup) and not fSecondPass then
  3684. if GrpCount < RegexMaxGroups - 1 then
  3685. begin
  3686. Inc(GrpCount);
  3687. GrpIndexes[GrpCount] := regNumBrackets;
  3688. if GrpName <> '' then
  3689. begin
  3690. if MatchIndexFromName(GrpName) >= 0 then
  3691. Error(reeNamedGroupDupName);
  3692. GrpNames[GrpCount] := GrpName;
  3693. end;
  3694. end;
  3695. ret := ParseReg(True, FlagTemp);
  3696. if ret = nil then
  3697. begin
  3698. Result := nil;
  3699. Exit;
  3700. end;
  3701. FlagParse := FlagParse or FlagTemp and (FLAG_HASWIDTH or FLAG_SPECSTART);
  3702. end;
  3703. gkLookbehindNeg:
  3704. begin
  3705. // don't make opcode
  3706. ret := EmitNode(OP_COMMENT);
  3707. FlagParse := FLAG_WORST;
  3708. end;
  3709. gkNamedGroupReference:
  3710. begin
  3711. Len := MatchIndexFromName(GrpName);
  3712. if Len < 0 then
  3713. Error(reeNamedGroupBadRef);
  3714. ret := EmitGroupRef(Len, fCompModifiers.I);
  3715. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3716. end;
  3717. gkModifierString:
  3718. begin
  3719. SavedPtr := regParse;
  3720. while (regParse < fRegexEnd) and (regParse^ <> ')') do
  3721. Inc(regParse);
  3722. if (regParse^ <> ')') or
  3723. not ParseModifiers(SavedPtr, regParse - SavedPtr, fCompModifiers) then
  3724. begin
  3725. Error(reeUnrecognizedModifier);
  3726. Exit;
  3727. end;
  3728. Inc(regParse); // skip ')'
  3729. ret := EmitNode(OP_COMMENT); // comment
  3730. // Error (reeQuantifFollowsNothing);
  3731. // Exit;
  3732. end;
  3733. gkComment:
  3734. begin
  3735. while (regParse < fRegexEnd) and (regParse^ <> ')') do
  3736. Inc(regParse);
  3737. if regParse^ <> ')' then
  3738. begin
  3739. Error(reeUnclosedComment);
  3740. Exit;
  3741. end;
  3742. Inc(regParse); // skip ')'
  3743. ret := EmitNode(OP_COMMENT); // comment
  3744. end;
  3745. gkRecursion:
  3746. begin
  3747. // set FLAG_HASWIDTH to allow compiling of such regex: b(?:m|(?R))*e
  3748. FlagParse := FlagParse or FLAG_HASWIDTH;
  3749. ret := EmitNode(OP_RECUR);
  3750. end;
  3751. gkSubCall:
  3752. begin
  3753. // set FLAG_HASWIDTH like for (?R)
  3754. FlagParse := FlagParse or FLAG_HASWIDTH;
  3755. ret := EmitNode(TReOp(Ord(OP_SUBCALL) + GrpIndex));
  3756. end;
  3757. end; // case GrpKind of
  3758. end;
  3759. '|', ')':
  3760. begin // Supposed to be caught earlier.
  3761. Error(reeInternalUrp);
  3762. Exit;
  3763. end;
  3764. '?', '+', '*':
  3765. begin
  3766. Error(reeQuantifFollowsNothing);
  3767. Exit;
  3768. end;
  3769. EscChar:
  3770. begin
  3771. if regParse >= fRegexEnd then
  3772. begin
  3773. Error(reeTrailingBackSlash);
  3774. Exit;
  3775. end;
  3776. case regParse^ of
  3777. 'b':
  3778. ret := EmitNode(OP_BOUND);
  3779. 'B':
  3780. ret := EmitNode(OP_NOTBOUND);
  3781. 'A':
  3782. ret := EmitNode(OP_BOL);
  3783. 'z':
  3784. ret := EmitNode(OP_EOL);
  3785. 'Z':
  3786. ret := EmitNode(OP_EOL2);
  3787. 'd':
  3788. begin // r.e.extension - any digit ('0' .. '9')
  3789. ret := EmitNode(OP_ANYDIGIT);
  3790. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3791. end;
  3792. 'D':
  3793. begin // r.e.extension - not digit ('0' .. '9')
  3794. ret := EmitNode(OP_NOTDIGIT);
  3795. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3796. end;
  3797. 's':
  3798. begin // r.e.extension - any space AnsiChar
  3799. ret := EmitNode(OP_ANYSPACE);
  3800. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3801. end;
  3802. 'S':
  3803. begin // r.e.extension - not space AnsiChar
  3804. ret := EmitNode(OP_NOTSPACE);
  3805. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3806. end;
  3807. 'w':
  3808. begin // r.e.extension - any english AnsiChar / digit / '_'
  3809. ret := EmitNode(OP_ANYLETTER);
  3810. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3811. end;
  3812. 'W':
  3813. begin // r.e.extension - not english AnsiChar / digit / '_'
  3814. ret := EmitNode(OP_NOTLETTER);
  3815. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3816. end;
  3817. 'v':
  3818. begin
  3819. ret := EmitNode(OP_ANYVERTSEP);
  3820. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3821. end;
  3822. 'V':
  3823. begin
  3824. ret := EmitNode(OP_NOTVERTSEP);
  3825. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3826. end;
  3827. 'h':
  3828. begin
  3829. ret := EmitNode(OP_ANYHORZSEP);
  3830. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3831. end;
  3832. 'H':
  3833. begin
  3834. ret := EmitNode(OP_NOTHORZSEP);
  3835. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3836. end;
  3837. '1' .. '9':
  3838. begin
  3839. ret := EmitGroupRef(Ord(regParse^) - Ord('0'), fCompModifiers.I);
  3840. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3841. end;
  3842. {$IFDEF FastUnicodeData}
  3843. 'p':
  3844. begin
  3845. ret := EmitCategoryMain(True);
  3846. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3847. end;
  3848. 'P':
  3849. begin
  3850. ret := EmitCategoryMain(False);
  3851. FlagParse := FlagParse or FLAG_HASWIDTH or FLAG_SIMPLE;
  3852. end;
  3853. {$ENDIF}
  3854. else
  3855. EmitExactly(UnQuoteChar(regParse, fRegexEnd));
  3856. end; { of case }
  3857. Inc(regParse);
  3858. end;
  3859. else
  3860. begin
  3861. Dec(regParse);
  3862. if fCompModifiers.X and // check for eXtended syntax
  3863. ((regParse^ = '#') or IsIgnoredChar(regParse^)) then
  3864. begin // ###0.941 \x
  3865. if regParse^ = '#' then
  3866. begin // Skip eXtended comment
  3867. // find comment terminator (group of \n and/or \r)
  3868. while (regParse < fRegexEnd) and (regParse^ <> #$d) and
  3869. (regParse^ <> #$a) do
  3870. Inc(regParse);
  3871. while (regParse^ = #$d) or (regParse^ = #$a)
  3872. // skip comment terminator
  3873. do
  3874. Inc(regParse);
  3875. // attempt to support different type of line separators
  3876. end
  3877. else
  3878. begin // Skip the blanks!
  3879. while IsIgnoredChar(regParse^) do
  3880. Inc(regParse);
  3881. end;
  3882. ret := EmitNode(OP_COMMENT); // comment
  3883. end
  3884. else
  3885. begin
  3886. Len := FindSkippedMetaLen(regParse, fRegexEnd);
  3887. if Len <= 0 then
  3888. if regParse^ <> '{' then
  3889. begin
  3890. Error(reeRarseAtomInternalDisaster);
  3891. Exit;
  3892. end
  3893. else
  3894. Len := FindSkippedMetaLen(regParse + 1, fRegexEnd) + 1;
  3895. // bad {n,m} - compile as EXACTLY
  3896. EnderChar := (regParse + Len)^;
  3897. if (Len > 1) and ((EnderChar = '*') or (EnderChar = '+') or (EnderChar = '?') or (EnderChar = '{')) then
  3898. Dec(Len); // back off clear of ?+*{ operand.
  3899. FlagParse := FlagParse or FLAG_HASWIDTH;
  3900. if Len = 1 then
  3901. FlagParse := FlagParse or FLAG_SIMPLE;
  3902. if fCompModifiers.I then
  3903. ret := EmitNode(OP_EXACTLYCI)
  3904. else
  3905. ret := EmitNode(OP_EXACTLY);
  3906. EmitInt(0);
  3907. while (Len > 0) and ((not fCompModifiers.X) or (regParse^ <> '#')) do
  3908. begin
  3909. if not fCompModifiers.X or not IsIgnoredChar(regParse^) then
  3910. begin
  3911. EmitC(regParse^);
  3912. if regCode <> @regDummy then
  3913. Inc(regExactlyLen^);
  3914. end;
  3915. Inc(regParse);
  3916. Dec(Len);
  3917. end;
  3918. end; { of if not comment }
  3919. end; { of case else }
  3920. end; { of case }
  3921. Result := ret;
  3922. end; { of function TRegExpr.ParseAtom
  3923. -------------------------------------------------------------- }
  3924. function TRegExpr.GetCompilerErrorPos: PtrInt;
  3925. begin
  3926. Result := 0;
  3927. if (fRegexStart = nil) or (regParse = nil) then
  3928. Exit; // not in compiling mode ?
  3929. Result := regParse - fRegexStart;
  3930. end; { of function TRegExpr.GetCompilerErrorPos
  3931. -------------------------------------------------------------- }
  3932. { ============================================================= }
  3933. { ===================== Matching section ====================== }
  3934. { ============================================================= }
  3935. procedure TRegExpr.FindGroupName(APtr, AEndPtr: PRegExprChar; AEndChar: REChar; var AName: RegExprString);
  3936. // check that group name is valid identifier, started from non-digit
  3937. // this is to be like in Python regex
  3938. var
  3939. P: PRegExprChar;
  3940. begin
  3941. P := APtr;
  3942. if IsDigitChar(P^) or not IsWordChar(P^) then
  3943. Error(reeNamedGroupBadName);
  3944. repeat
  3945. if P >= AEndPtr then
  3946. Error(reeNamedGroupBad);
  3947. if P^ = AEndChar then
  3948. Break;
  3949. if not IsWordChar(P^) then
  3950. Error(reeNamedGroupBadName);
  3951. Inc(P);
  3952. until False;
  3953. SetString(AName, APtr, P-APtr);
  3954. end;
  3955. function TRegExpr.FindRepeated(p: PRegExprChar; AMax: integer): integer;
  3956. // repeatedly match something simple, report how many
  3957. // p: points to current opcode
  3958. var
  3959. scan: PRegExprChar;
  3960. opnd: PRegExprChar;
  3961. TheMax: PtrInt; // PtrInt, gets diff of 2 pointers
  3962. InvChar: REChar;
  3963. CurStart, CurEnd: PRegExprChar;
  3964. ArrayIndex: integer;
  3965. {$IFDEF UnicodeEx}
  3966. i: integer;
  3967. {$ENDIF}
  3968. begin
  3969. Result := 0;
  3970. scan := regInput; // points into InputString
  3971. opnd := p + REOpSz + RENextOffSz; // points to operand of opcode (after OP_nnn code)
  3972. TheMax := fInputEnd - scan;
  3973. if TheMax > AMax then
  3974. TheMax := AMax;
  3975. case PREOp(p)^ of
  3976. OP_ANY:
  3977. begin
  3978. // note - OP_ANYML cannot be proceeded in FindRepeated because can skip
  3979. // more than one AnsiChar at once
  3980. {$IFDEF UnicodeEx}
  3981. for i := 1 to TheMax do
  3982. IncUnicode2(scan, Result);
  3983. {$ELSE}
  3984. Result := TheMax;
  3985. Inc(scan, Result);
  3986. {$ENDIF}
  3987. end;
  3988. OP_EXACTLY:
  3989. begin // in opnd can be only ONE AnsiChar !!!
  3990. {
  3991. // Alexey: commented because of https://github.com/andgineer/TRegExpr/issues/145
  3992. NLen := PLongInt(opnd)^;
  3993. if TheMax > NLen then
  3994. TheMax := NLen;
  3995. }
  3996. Inc(opnd, RENumberSz);
  3997. while (Result < TheMax) and (opnd^ = scan^) do
  3998. begin
  3999. Inc(Result);
  4000. Inc(scan);
  4001. end;
  4002. end;
  4003. OP_EXACTLYCI:
  4004. begin // in opnd can be only ONE AnsiChar !!!
  4005. {
  4006. // Alexey: commented because of https://github.com/andgineer/TRegExpr/issues/145
  4007. NLen := PLongInt(opnd)^;
  4008. if TheMax > NLen then
  4009. TheMax := NLen;
  4010. }
  4011. Inc(opnd, RENumberSz);
  4012. while (Result < TheMax) and (opnd^ = scan^) do
  4013. begin // prevent unneeded InvertCase //###0.931
  4014. Inc(Result);
  4015. Inc(scan);
  4016. end;
  4017. if Result < TheMax then
  4018. begin // ###0.931
  4019. InvChar := InvertCase(opnd^); // store in register
  4020. while (Result < TheMax) and ((opnd^ = scan^) or (InvChar = scan^)) do
  4021. begin
  4022. Inc(Result);
  4023. Inc(scan);
  4024. end;
  4025. end;
  4026. end;
  4027. OP_BSUBEXP:
  4028. begin // ###0.936
  4029. ArrayIndex := GrpIndexes[Ord(opnd^)];
  4030. if ArrayIndex < 0 then
  4031. Exit;
  4032. CurStart := GrpBounds[regRecursion].GrpStart[ArrayIndex];
  4033. if CurStart = nil then
  4034. Exit;
  4035. CurEnd := GrpBounds[regRecursion].GrpEnd[ArrayIndex];
  4036. if CurEnd = nil then
  4037. Exit;
  4038. repeat
  4039. opnd := CurStart;
  4040. while opnd < CurEnd do
  4041. begin
  4042. if (scan >= fInputEnd) or (scan^ <> opnd^) then
  4043. Exit;
  4044. Inc(scan);
  4045. Inc(opnd);
  4046. end;
  4047. Inc(Result);
  4048. regInput := scan;
  4049. until Result >= AMax;
  4050. end;
  4051. OP_BSUBEXPCI:
  4052. begin // ###0.936
  4053. ArrayIndex := GrpIndexes[Ord(opnd^)];
  4054. if ArrayIndex < 0 then
  4055. Exit;
  4056. CurStart := GrpBounds[regRecursion].GrpStart[ArrayIndex];
  4057. if CurStart = nil then
  4058. Exit;
  4059. CurEnd := GrpBounds[regRecursion].GrpEnd[ArrayIndex];
  4060. if CurEnd = nil then
  4061. Exit;
  4062. repeat
  4063. opnd := CurStart;
  4064. while opnd < CurEnd do
  4065. begin
  4066. if (scan >= fInputEnd) or
  4067. ((scan^ <> opnd^) and (scan^ <> InvertCase(opnd^))) then
  4068. Exit;
  4069. Inc(scan);
  4070. Inc(opnd);
  4071. end;
  4072. Inc(Result);
  4073. regInput := scan;
  4074. until Result >= AMax;
  4075. end;
  4076. OP_ANYDIGIT:
  4077. while (Result < TheMax) and IsDigitChar(scan^) do
  4078. begin
  4079. Inc(Result);
  4080. Inc(scan);
  4081. end;
  4082. OP_NOTDIGIT:
  4083. {$IFDEF UNICODEEX}
  4084. begin
  4085. i := 0;
  4086. while (i < TheMax) and not IsDigitChar(scan^) do
  4087. begin
  4088. Inc(i);
  4089. IncUnicode2(scan, Result);
  4090. end;
  4091. end;
  4092. {$ELSE}
  4093. while (Result < TheMax) and not IsDigitChar(scan^) do
  4094. begin
  4095. Inc(Result);
  4096. Inc(scan);
  4097. end;
  4098. {$ENDIF}
  4099. OP_ANYLETTER:
  4100. while (Result < TheMax) and IsWordChar(scan^) do // ###0.940
  4101. begin
  4102. Inc(Result);
  4103. Inc(scan);
  4104. end;
  4105. OP_NOTLETTER:
  4106. {$IFDEF UNICODEEX}
  4107. begin
  4108. i := 0;
  4109. while (i < TheMax) and not IsWordChar(scan^) do
  4110. begin
  4111. Inc(i);
  4112. IncUnicode2(scan, Result);
  4113. end;
  4114. end;
  4115. {$ELSE}
  4116. while (Result < TheMax) and not IsWordChar(scan^) do // ###0.940
  4117. begin
  4118. Inc(Result);
  4119. Inc(scan);
  4120. end;
  4121. {$ENDIF}
  4122. OP_ANYSPACE:
  4123. while (Result < TheMax) and IsSpaceChar(scan^) do
  4124. begin
  4125. Inc(Result);
  4126. Inc(scan);
  4127. end;
  4128. OP_NOTSPACE:
  4129. {$IFDEF UNICODEEX}
  4130. begin
  4131. i := 0;
  4132. while (i < TheMax) and not IsSpaceChar(scan^) do
  4133. begin
  4134. Inc(i);
  4135. IncUnicode2(scan, Result);
  4136. end;
  4137. end;
  4138. {$ELSE}
  4139. while (Result < TheMax) and not IsSpaceChar(scan^) do
  4140. begin
  4141. Inc(Result);
  4142. Inc(scan);
  4143. end;
  4144. {$ENDIF}
  4145. OP_ANYVERTSEP:
  4146. while (Result < TheMax) and IsVertLineSeparator(scan^) do
  4147. begin
  4148. Inc(Result);
  4149. Inc(scan);
  4150. end;
  4151. OP_NOTVERTSEP:
  4152. {$IFDEF UNICODEEX}
  4153. begin
  4154. i := 0;
  4155. while (i < TheMax) and not IsVertLineSeparator(scan^) do
  4156. begin
  4157. Inc(i);
  4158. IncUnicode2(scan, Result);
  4159. end;
  4160. end;
  4161. {$ELSE}
  4162. while (Result < TheMax) and not IsVertLineSeparator(scan^) do
  4163. begin
  4164. Inc(Result);
  4165. Inc(scan);
  4166. end;
  4167. {$ENDIF}
  4168. OP_ANYHORZSEP:
  4169. while (Result < TheMax) and IsHorzSeparator(scan^) do
  4170. begin
  4171. Inc(Result);
  4172. Inc(scan);
  4173. end;
  4174. OP_NOTHORZSEP:
  4175. {$IFDEF UNICODEEX}
  4176. begin
  4177. i := 0;
  4178. while (i < TheMax) and not IsHorzSeparator(scan^) do
  4179. begin
  4180. Inc(i);
  4181. IncUnicode2(scan, Result);
  4182. end;
  4183. end;
  4184. {$ELSE}
  4185. while (Result < TheMax) and not IsHorzSeparator(scan^) do
  4186. begin
  4187. Inc(Result);
  4188. Inc(scan);
  4189. end;
  4190. {$ENDIF}
  4191. OP_ANYOF:
  4192. {$IFDEF UNICODEEX}
  4193. begin
  4194. i := 0;
  4195. while (i < TheMax) and FindInCharClass(opnd, scan^, False) do
  4196. begin
  4197. Inc(i);
  4198. IncUnicode2(scan, Result);
  4199. end;
  4200. end;
  4201. {$ELSE}
  4202. while (Result < TheMax) and FindInCharClass(opnd, scan^, False) do
  4203. begin
  4204. Inc(Result);
  4205. Inc(scan);
  4206. end;
  4207. {$ENDIF}
  4208. OP_ANYBUT:
  4209. {$IFDEF UNICODEEX}
  4210. begin
  4211. i := 0;
  4212. while (i < TheMax) and not FindInCharClass(opnd, scan^, False) do
  4213. begin
  4214. Inc(i);
  4215. IncUnicode2(scan, Result);
  4216. end;
  4217. end;
  4218. {$ELSE}
  4219. while (Result < TheMax) and not FindInCharClass(opnd, scan^, False) do
  4220. begin
  4221. Inc(Result);
  4222. Inc(scan);
  4223. end;
  4224. {$ENDIF}
  4225. OP_ANYOFCI:
  4226. {$IFDEF UNICODEEX}
  4227. begin
  4228. i := 0;
  4229. while (i < TheMax) and FindInCharClass(opnd, scan^, True) do
  4230. begin
  4231. Inc(i);
  4232. IncUnicode2(scan, Result);
  4233. end;
  4234. end;
  4235. {$ELSE}
  4236. while (Result < TheMax) and FindInCharClass(opnd, scan^, True) do
  4237. begin
  4238. Inc(Result);
  4239. Inc(scan);
  4240. end;
  4241. {$ENDIF}
  4242. OP_ANYBUTCI:
  4243. {$IFDEF UNICODEEX}
  4244. begin
  4245. i := 0;
  4246. while (i < TheMax) and not FindInCharClass(opnd, scan^, True) do
  4247. begin
  4248. Inc(i);
  4249. IncUnicode2(scan, Result);
  4250. end;
  4251. end;
  4252. {$ELSE}
  4253. while (Result < TheMax) and not FindInCharClass(opnd, scan^, True) do
  4254. begin
  4255. Inc(Result);
  4256. Inc(scan);
  4257. end;
  4258. {$ENDIF}
  4259. {$IFDEF FastUnicodeData}
  4260. OP_ANYCATEGORY:
  4261. {$IFDEF UNICODEEX}
  4262. begin
  4263. i := 0;
  4264. while (i < TheMax) and MatchOneCharCategory(opnd, scan) do
  4265. begin
  4266. Inc(i);
  4267. IncUnicode2(scan, Result);
  4268. end;
  4269. end;
  4270. {$ELSE}
  4271. while (Result < TheMax) and MatchOneCharCategory(opnd, scan) do
  4272. begin
  4273. Inc(Result);
  4274. Inc(scan);
  4275. end;
  4276. {$ENDIF}
  4277. OP_NOTCATEGORY:
  4278. {$IFDEF UNICODEEX}
  4279. begin
  4280. i := 0;
  4281. while (i < TheMax) and not MatchOneCharCategory(opnd, scan) do
  4282. begin
  4283. Inc(i);
  4284. IncUnicode2(scan, Result);
  4285. end;
  4286. end;
  4287. {$ELSE}
  4288. while (Result < TheMax) and not MatchOneCharCategory(opnd, scan) do
  4289. begin
  4290. Inc(Result);
  4291. Inc(scan);
  4292. end;
  4293. {$ENDIF}
  4294. {$ENDIF}
  4295. else
  4296. begin // Oh dear. Called inappropriately.
  4297. Result := 0;
  4298. Error(reeRegRepeatCalledInappropriately);
  4299. Exit;
  4300. end;
  4301. end; { of case }
  4302. regInput := scan;
  4303. end; { of function TRegExpr.FindRepeated
  4304. -------------------------------------------------------------- }
  4305. function TRegExpr.regNext(p: PRegExprChar): PRegExprChar;
  4306. // dig the "next" pointer out of a node
  4307. var
  4308. offset: TRENextOff;
  4309. begin
  4310. if p = @regDummy then
  4311. begin
  4312. Result := nil;
  4313. Exit;
  4314. end;
  4315. offset := PRENextOff(AlignToPtr(p + REOpSz))^; // ###0.933 inlined NEXT
  4316. if offset = 0 then
  4317. Result := nil
  4318. else
  4319. Result := p + offset;
  4320. end; { of function TRegExpr.regNext
  4321. -------------------------------------------------------------- }
  4322. function TRegExpr.MatchPrim(prog: PRegExprChar): boolean;
  4323. // recursively matching routine
  4324. // Conceptually the strategy is simple: check to see whether the current
  4325. // node matches, call self recursively to see whether the rest matches,
  4326. // and then act accordingly. In practice we make some effort to avoid
  4327. // recursion, in particular by going through "ordinary" nodes (that don't
  4328. // need to know whether the rest of the match failed) by a loop instead of
  4329. // by recursion.
  4330. var
  4331. scan: PRegExprChar; // current node
  4332. next: PRegExprChar; // next node
  4333. Len: PtrInt;
  4334. opnd, opGrpEnd: PRegExprChar;
  4335. no: integer;
  4336. save: PRegExprChar;
  4337. saveCurrentGrp: integer;
  4338. nextch: REChar;
  4339. BracesMin, BracesMax: integer;
  4340. // we use integer instead of TREBracesArg to better support */+
  4341. {$IFDEF ComplexBraces}
  4342. SavedLoopStack: TRegExprLoopStack; // very bad for recursion
  4343. SavedLoopStackIdx: integer;
  4344. {$ENDIF}
  4345. bound1, bound2: boolean;
  4346. checkAtomicGroup: boolean;
  4347. begin
  4348. Result := False;
  4349. {
  4350. // Alexey: not sure it's ok for long searches in big texts, so disabled
  4351. if regNestedCalls > MaxRegexBackTracking then
  4352. Exit;
  4353. Inc(regNestedCalls);
  4354. }
  4355. scan := prog;
  4356. while scan <> nil do
  4357. begin
  4358. Len := PRENextOff(AlignToPtr(scan + 1))^; // ###0.932 inlined regNext
  4359. if Len = 0 then
  4360. next := nil
  4361. else
  4362. next := scan + Len;
  4363. case scan^ of
  4364. OP_BOUND:
  4365. begin
  4366. bound1 := (regInput = fInputStart) or not IsWordChar((regInput - 1)^);
  4367. bound2 := (regInput >= fInputEnd) or not IsWordChar(regInput^);
  4368. if bound1 = bound2 then
  4369. Exit;
  4370. end;
  4371. OP_NOTBOUND:
  4372. begin
  4373. bound1 := (regInput = fInputStart) or not IsWordChar((regInput - 1)^);
  4374. bound2 := (regInput >= fInputEnd) or not IsWordChar(regInput^);
  4375. if bound1 <> bound2 then
  4376. Exit;
  4377. end;
  4378. OP_BOL:
  4379. begin
  4380. if regInput <> fInputStart then
  4381. Exit;
  4382. end;
  4383. OP_EOL:
  4384. begin
  4385. // \z matches at the very end
  4386. if regInput < fInputEnd then
  4387. Exit;
  4388. end;
  4389. OP_EOL2:
  4390. begin
  4391. // \Z matches at the very and + before the final line-break (LF and CR LF)
  4392. if regInput < fInputEnd then
  4393. begin
  4394. if (regInput = fInputEnd - 1) and (regInput^ = #10) then
  4395. begin end
  4396. else
  4397. if (regInput = fInputEnd - 2) and (regInput^ = #13) and ((regInput + 1) ^ = #10) then
  4398. begin end
  4399. else
  4400. Exit;
  4401. end;
  4402. end;
  4403. OP_BOLML:
  4404. if regInput > fInputStart then
  4405. begin
  4406. if ((regInput - 1) <= fInputStart) or
  4407. not IsPairedBreak(regInput - 2) then
  4408. begin
  4409. // don't stop between paired separator
  4410. if IsPairedBreak(regInput - 1) then
  4411. Exit;
  4412. if not IsCustomLineSeparator((regInput - 1)^) then
  4413. Exit;
  4414. end;
  4415. end;
  4416. OP_EOLML:
  4417. if regInput < fInputEnd then
  4418. begin
  4419. if not IsPairedBreak(regInput) then
  4420. begin
  4421. // don't stop between paired separator
  4422. if (regInput > fInputStart) and IsPairedBreak(regInput - 1) then
  4423. Exit;
  4424. if not IsCustomLineSeparator(regInput^) then
  4425. Exit;
  4426. end;
  4427. end;
  4428. OP_ANY:
  4429. begin
  4430. if regInput >= fInputEnd then
  4431. Exit;
  4432. {$IFDEF UNICODEEX}
  4433. IncUnicode(regInput);
  4434. {$ELSE}
  4435. Inc(regInput);
  4436. {$ENDIF}
  4437. end;
  4438. OP_ANYML:
  4439. begin
  4440. if (regInput >= fInputEnd) or
  4441. IsPairedBreak(regInput) or
  4442. IsCustomLineSeparator(regInput^)
  4443. then
  4444. Exit;
  4445. {$IFDEF UNICODEEX}
  4446. IncUnicode(regInput);
  4447. {$ELSE}
  4448. Inc(regInput);
  4449. {$ENDIF}
  4450. end;
  4451. OP_ANYDIGIT:
  4452. begin
  4453. if (regInput >= fInputEnd) or not IsDigitChar(regInput^) then
  4454. Exit;
  4455. Inc(regInput);
  4456. end;
  4457. OP_NOTDIGIT:
  4458. begin
  4459. if (regInput >= fInputEnd) or IsDigitChar(regInput^) then
  4460. Exit;
  4461. {$IFDEF UNICODEEX}
  4462. IncUnicode(regInput);
  4463. {$ELSE}
  4464. Inc(regInput);
  4465. {$ENDIF}
  4466. end;
  4467. OP_ANYLETTER:
  4468. begin
  4469. if (regInput >= fInputEnd) or not IsWordChar(regInput^) then
  4470. Exit;
  4471. Inc(regInput);
  4472. end;
  4473. OP_NOTLETTER:
  4474. begin
  4475. if (regInput >= fInputEnd) or IsWordChar(regInput^) then
  4476. Exit;
  4477. {$IFDEF UNICODEEX}
  4478. IncUnicode(regInput);
  4479. {$ELSE}
  4480. Inc(regInput);
  4481. {$ENDIF}
  4482. end;
  4483. OP_ANYSPACE:
  4484. begin
  4485. if (regInput >= fInputEnd) or not IsSpaceChar(regInput^) then
  4486. Exit;
  4487. Inc(regInput);
  4488. end;
  4489. OP_NOTSPACE:
  4490. begin
  4491. if (regInput >= fInputEnd) or IsSpaceChar(regInput^) then
  4492. Exit;
  4493. {$IFDEF UNICODEEX}
  4494. IncUnicode(regInput);
  4495. {$ELSE}
  4496. Inc(regInput);
  4497. {$ENDIF}
  4498. end;
  4499. OP_ANYVERTSEP:
  4500. begin
  4501. if (regInput >= fInputEnd) or not IsVertLineSeparator(regInput^) then
  4502. Exit;
  4503. Inc(regInput);
  4504. end;
  4505. OP_NOTVERTSEP:
  4506. begin
  4507. if (regInput >= fInputEnd) or IsVertLineSeparator(regInput^) then
  4508. Exit;
  4509. {$IFDEF UNICODEEX}
  4510. IncUnicode(regInput);
  4511. {$ELSE}
  4512. Inc(regInput);
  4513. {$ENDIF}
  4514. end;
  4515. OP_ANYHORZSEP:
  4516. begin
  4517. if (regInput >= fInputEnd) or not IsHorzSeparator(regInput^) then
  4518. Exit;
  4519. Inc(regInput);
  4520. end;
  4521. OP_NOTHORZSEP:
  4522. begin
  4523. if (regInput >= fInputEnd) or IsHorzSeparator(regInput^) then
  4524. Exit;
  4525. {$IFDEF UNICODEEX}
  4526. IncUnicode(regInput);
  4527. {$ELSE}
  4528. Inc(regInput);
  4529. {$ENDIF}
  4530. end;
  4531. OP_EXACTLYCI:
  4532. begin
  4533. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  4534. Len := PLongInt(opnd)^;
  4535. Inc(opnd, RENumberSz);
  4536. // Inline the first character, for speed.
  4537. if (opnd^ <> regInput^) and (InvertCase(opnd^) <> regInput^) then
  4538. Exit;
  4539. // ###0.929 begin
  4540. no := Len;
  4541. save := regInput;
  4542. while no > 1 do
  4543. begin
  4544. Inc(save);
  4545. Inc(opnd);
  4546. if (opnd^ <> save^) and (InvertCase(opnd^) <> save^) then
  4547. Exit;
  4548. Dec(no);
  4549. end;
  4550. // ###0.929 end
  4551. Inc(regInput, Len);
  4552. end;
  4553. OP_EXACTLY:
  4554. begin
  4555. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  4556. Len := PLongInt(opnd)^;
  4557. Inc(opnd, RENumberSz);
  4558. // Inline the first character, for speed.
  4559. if opnd^ <> regInput^ then
  4560. Exit;
  4561. // ###0.929 begin
  4562. no := Len;
  4563. save := regInput;
  4564. while no > 1 do
  4565. begin
  4566. Inc(save);
  4567. Inc(opnd);
  4568. if opnd^ <> save^ then
  4569. Exit;
  4570. Dec(no);
  4571. end;
  4572. // ###0.929 end
  4573. Inc(regInput, Len);
  4574. end;
  4575. OP_BSUBEXP:
  4576. begin // ###0.936
  4577. no := Ord((scan + REOpSz + RENextOffSz)^);
  4578. no := GrpIndexes[no];
  4579. if no < 0 then
  4580. Exit;
  4581. opnd := GrpBounds[regRecursion].GrpStart[no];
  4582. if opnd = nil then
  4583. Exit;
  4584. opGrpEnd := GrpBounds[regRecursion].GrpEnd[no];
  4585. if opGrpEnd = nil then
  4586. Exit;
  4587. save := regInput;
  4588. while opnd < opGrpEnd do
  4589. begin
  4590. if (save >= fInputEnd) or (save^ <> opnd^) then
  4591. Exit;
  4592. Inc(save);
  4593. Inc(opnd);
  4594. end;
  4595. regInput := save;
  4596. end;
  4597. OP_BSUBEXPCI:
  4598. begin // ###0.936
  4599. no := Ord((scan + REOpSz + RENextOffSz)^);
  4600. no := GrpIndexes[no];
  4601. if no < 0 then
  4602. Exit;
  4603. opnd := GrpBounds[regRecursion].GrpStart[no];
  4604. if opnd = nil then
  4605. Exit;
  4606. opGrpEnd := GrpBounds[regRecursion].GrpEnd[no];
  4607. if opGrpEnd = nil then
  4608. Exit;
  4609. save := regInput;
  4610. while opnd < opGrpEnd do
  4611. begin
  4612. if (save >= fInputEnd) or
  4613. ((save^ <> opnd^) and (save^ <> InvertCase(opnd^))) then
  4614. Exit;
  4615. Inc(save);
  4616. Inc(opnd);
  4617. end;
  4618. regInput := save;
  4619. end;
  4620. OP_ANYOF:
  4621. begin
  4622. if (regInput >= fInputEnd) or
  4623. not FindInCharClass(scan + REOpSz + RENextOffSz, regInput^, False) then
  4624. Exit;
  4625. {$IFDEF UNICODEEX}
  4626. IncUnicode(regInput);
  4627. {$ELSE}
  4628. Inc(regInput);
  4629. {$ENDIF}
  4630. end;
  4631. OP_ANYBUT:
  4632. begin
  4633. if (regInput >= fInputEnd) or
  4634. FindInCharClass(scan + REOpSz + RENextOffSz, regInput^, False) then
  4635. Exit;
  4636. {$IFDEF UNICODEEX}
  4637. IncUnicode(regInput);
  4638. {$ELSE}
  4639. Inc(regInput);
  4640. {$ENDIF}
  4641. end;
  4642. OP_ANYOFCI:
  4643. begin
  4644. if (regInput >= fInputEnd) or
  4645. not FindInCharClass(scan + REOpSz + RENextOffSz, regInput^, True) then
  4646. Exit;
  4647. {$IFDEF UNICODEEX}
  4648. IncUnicode(regInput);
  4649. {$ELSE}
  4650. Inc(regInput);
  4651. {$ENDIF}
  4652. end;
  4653. OP_ANYBUTCI:
  4654. begin
  4655. if (regInput >= fInputEnd) or
  4656. FindInCharClass(scan + REOpSz + RENextOffSz, regInput^, True) then
  4657. Exit;
  4658. {$IFDEF UNICODEEX}
  4659. IncUnicode(regInput);
  4660. {$ELSE}
  4661. Inc(regInput);
  4662. {$ENDIF}
  4663. end;
  4664. OP_NOTHING:
  4665. ;
  4666. OP_COMMENT:
  4667. ;
  4668. OP_BACK:
  4669. ;
  4670. OP_OPEN_FIRST .. OP_OPEN_LAST:
  4671. begin
  4672. no := Ord(scan^) - Ord(OP_OPEN);
  4673. regCurrentGrp := no;
  4674. save := GrpBounds[regRecursion].GrpStart[no];
  4675. GrpBounds[regRecursion].GrpStart[no] := regInput;
  4676. Result := MatchPrim(next);
  4677. if not Result then
  4678. GrpBounds[regRecursion].GrpStart[no] := save;
  4679. // handle negative lookahead
  4680. if regLookaheadNeg then
  4681. if no = regLookaheadGroup then
  4682. begin
  4683. Result := not Result;
  4684. if Result then
  4685. begin
  4686. // we need zero length of "lookahead group",
  4687. // it is later used to adjust the match
  4688. GrpBounds[regRecursion].GrpStart[no] := regInput;
  4689. GrpBounds[regRecursion].GrpEnd[no]:= regInput;
  4690. end
  4691. else
  4692. GrpBounds[regRecursion].GrpStart[no] := save;
  4693. end;
  4694. Exit;
  4695. end;
  4696. OP_CLOSE_FIRST .. OP_CLOSE_LAST:
  4697. begin
  4698. no := Ord(scan^) - Ord(OP_CLOSE);
  4699. regCurrentGrp := -1;
  4700. // handle atomic group, mark it as "done"
  4701. // (we are here because some OP_BRANCH is matched)
  4702. if GrpAtomic[no] then
  4703. GrpAtomicDone[no] := True;
  4704. save := GrpBounds[regRecursion].GrpEnd[no];
  4705. GrpBounds[regRecursion].GrpEnd[no] := regInput;
  4706. // if we are in OP_SUBCALL* call, it called OP_OPEN*, so we must return
  4707. // in OP_CLOSE, without going to next opcode
  4708. if GrpSubCalled[no] then
  4709. begin
  4710. Result := True;
  4711. Exit;
  4712. end;
  4713. Result := MatchPrim(next);
  4714. if not Result then // ###0.936
  4715. GrpBounds[regRecursion].GrpEnd[no] := save;
  4716. Exit;
  4717. end;
  4718. OP_BRANCH:
  4719. begin
  4720. saveCurrentGrp := regCurrentGrp;
  4721. checkAtomicGroup := (regCurrentGrp >= 0) and GrpAtomic[regCurrentGrp];
  4722. if (next^ <> OP_BRANCH) // No next choice in group
  4723. then
  4724. next := scan + REOpSz + RENextOffSz // Avoid recursion
  4725. else
  4726. begin
  4727. repeat
  4728. save := regInput;
  4729. Result := MatchPrim(scan + REOpSz + RENextOffSz);
  4730. regCurrentGrp := saveCurrentGrp;
  4731. if Result then
  4732. Exit;
  4733. // if branch worked until OP_CLOSE, and marked atomic group as "done", then exit
  4734. if checkAtomicGroup then
  4735. if GrpAtomicDone[regCurrentGrp] then
  4736. Exit;
  4737. regInput := save;
  4738. scan := regNext(scan);
  4739. until (scan = nil) or (scan^ <> OP_BRANCH);
  4740. Exit;
  4741. end;
  4742. end;
  4743. {$IFDEF ComplexBraces}
  4744. OP_LOOPENTRY:
  4745. begin // ###0.925
  4746. no := LoopStackIdx;
  4747. Inc(LoopStackIdx);
  4748. if LoopStackIdx > LoopStackMax then
  4749. begin
  4750. Error(reeLoopStackExceeded);
  4751. Exit;
  4752. end;
  4753. save := regInput;
  4754. LoopStack[LoopStackIdx] := 0; // init loop counter
  4755. Result := MatchPrim(next); // execute loop
  4756. LoopStackIdx := no; // cleanup
  4757. if Result then
  4758. Exit;
  4759. regInput := save;
  4760. Exit;
  4761. end;
  4762. OP_LOOP, OP_LOOPNG:
  4763. begin // ###0.940
  4764. if LoopStackIdx <= 0 then
  4765. begin
  4766. Error(reeLoopWithoutEntry);
  4767. Exit;
  4768. end;
  4769. opnd := scan + PRENextOff(AlignToPtr(scan + REOpSz + RENextOffSz + 2 * REBracesArgSz))^;
  4770. BracesMin := PREBracesArg(AlignToInt(scan + REOpSz + RENextOffSz))^;
  4771. BracesMax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  4772. save := regInput;
  4773. if LoopStack[LoopStackIdx] >= BracesMin then
  4774. begin // Min alredy matched - we can work
  4775. if scan^ = OP_LOOP then
  4776. begin
  4777. // greedy way - first try to max deep of greed ;)
  4778. if LoopStack[LoopStackIdx] < BracesMax then
  4779. begin
  4780. Inc(LoopStack[LoopStackIdx]);
  4781. no := LoopStackIdx;
  4782. Result := MatchPrim(opnd);
  4783. LoopStackIdx := no;
  4784. if Result then
  4785. Exit;
  4786. regInput := save;
  4787. end;
  4788. Dec(LoopStackIdx); // Fail. May be we are too greedy? ;)
  4789. Result := MatchPrim(next);
  4790. if not Result then
  4791. regInput := save;
  4792. Exit;
  4793. end
  4794. else
  4795. begin
  4796. // non-greedy - try just now
  4797. Result := MatchPrim(next);
  4798. if Result then
  4799. Exit
  4800. else
  4801. regInput := save; // failed - move next and try again
  4802. if LoopStack[LoopStackIdx] < BracesMax then
  4803. begin
  4804. Inc(LoopStack[LoopStackIdx]);
  4805. no := LoopStackIdx;
  4806. Result := MatchPrim(opnd);
  4807. LoopStackIdx := no;
  4808. if Result then
  4809. Exit;
  4810. regInput := save;
  4811. end;
  4812. Dec(LoopStackIdx); // Failed - back up
  4813. Exit;
  4814. end
  4815. end
  4816. else
  4817. begin // first match a min_cnt times
  4818. Inc(LoopStack[LoopStackIdx]);
  4819. no := LoopStackIdx;
  4820. Result := MatchPrim(opnd);
  4821. LoopStackIdx := no;
  4822. if Result then
  4823. Exit;
  4824. Dec(LoopStack[LoopStackIdx]);
  4825. regInput := save;
  4826. Exit;
  4827. end;
  4828. end;
  4829. {$ENDIF}
  4830. OP_STAR, OP_PLUS, OP_BRACES, OP_STARNG, OP_PLUSNG, OP_BRACESNG:
  4831. begin
  4832. // Lookahead to avoid useless match attempts when we know
  4833. // what character comes next.
  4834. nextch := #0;
  4835. if next^ = OP_EXACTLY then
  4836. nextch := (next + REOpSz + RENextOffSz + RENumberSz)^;
  4837. BracesMax := MaxInt; // infinite loop for * and + //###0.92
  4838. if (scan^ = OP_STAR) or (scan^ = OP_STARNG) then
  4839. BracesMin := 0 // star
  4840. else if (scan^ = OP_PLUS) or (scan^ = OP_PLUSNG) then
  4841. BracesMin := 1 // plus
  4842. else
  4843. begin // braces
  4844. BracesMin := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  4845. BracesMax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  4846. end;
  4847. save := regInput;
  4848. opnd := scan + REOpSz + RENextOffSz;
  4849. if (scan^ = OP_BRACES) or (scan^ = OP_BRACESNG) then
  4850. Inc(opnd, 2 * REBracesArgSz);
  4851. if (scan^ = OP_PLUSNG) or (scan^ = OP_STARNG) or (scan^ = OP_BRACESNG) then
  4852. begin
  4853. // non-greedy mode
  4854. BracesMax := FindRepeated(opnd, BracesMax);
  4855. // don't repeat more than BracesMax
  4856. // Now we know real Max limit to move forward (for recursion 'back up')
  4857. // In some cases it can be faster to check only Min positions first,
  4858. // but after that we have to check every position separtely instead
  4859. // of fast scannig in loop.
  4860. no := BracesMin;
  4861. while no <= BracesMax do
  4862. begin
  4863. regInput := save + no;
  4864. // If it could work, try it.
  4865. if (nextch = #0) or (regInput^ = nextch) then
  4866. begin
  4867. {$IFDEF ComplexBraces}
  4868. System.Move(LoopStack, SavedLoopStack, SizeOf(LoopStack));
  4869. // ###0.925
  4870. SavedLoopStackIdx := LoopStackIdx;
  4871. {$ENDIF}
  4872. if MatchPrim(next) then
  4873. begin
  4874. Result := True;
  4875. Exit;
  4876. end;
  4877. {$IFDEF ComplexBraces}
  4878. System.Move(SavedLoopStack, LoopStack, SizeOf(LoopStack));
  4879. LoopStackIdx := SavedLoopStackIdx;
  4880. {$ENDIF}
  4881. end;
  4882. Inc(no); // Couldn't or didn't - move forward.
  4883. end; { of while }
  4884. Exit;
  4885. end
  4886. else
  4887. begin // greedy mode
  4888. no := FindRepeated(opnd, BracesMax); // don't repeat more than max_cnt
  4889. while no >= BracesMin do
  4890. begin
  4891. // If it could work, try it.
  4892. if (nextch = #0) or (regInput^ = nextch) then
  4893. begin
  4894. {$IFDEF ComplexBraces}
  4895. System.Move(LoopStack, SavedLoopStack, SizeOf(LoopStack));
  4896. // ###0.925
  4897. SavedLoopStackIdx := LoopStackIdx;
  4898. {$ENDIF}
  4899. if MatchPrim(next) then
  4900. begin
  4901. Result := True;
  4902. Exit;
  4903. end;
  4904. {$IFDEF ComplexBraces}
  4905. System.Move(SavedLoopStack, LoopStack, SizeOf(LoopStack));
  4906. LoopStackIdx := SavedLoopStackIdx;
  4907. {$ENDIF}
  4908. end;
  4909. Dec(no); // Couldn't or didn't - back up.
  4910. regInput := save + no;
  4911. end; { of while }
  4912. Exit;
  4913. end;
  4914. end;
  4915. OP_STAR_POSS, OP_PLUS_POSS, OP_BRACES_POSS:
  4916. begin
  4917. // Lookahead to avoid useless match attempts when we know
  4918. // what character comes next.
  4919. nextch := #0;
  4920. if next^ = OP_EXACTLY then
  4921. nextch := (next + REOpSz + RENextOffSz + RENumberSz)^;
  4922. opnd := scan + REOpSz + RENextOffSz;
  4923. case scan^ of
  4924. OP_STAR_POSS:
  4925. begin
  4926. BracesMin := 0;
  4927. BracesMax := MaxInt;
  4928. end;
  4929. OP_PLUS_POSS:
  4930. begin
  4931. BracesMin := 1;
  4932. BracesMax := MaxInt;
  4933. end;
  4934. else
  4935. begin // braces
  4936. BracesMin := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  4937. BracesMax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  4938. Inc(opnd, 2 * REBracesArgSz);
  4939. end;
  4940. end;
  4941. no := FindRepeated(opnd, BracesMax);
  4942. if no >= BracesMin then
  4943. if (nextch = #0) or (regInput^ = nextch) then
  4944. Result := MatchPrim(next);
  4945. Exit;
  4946. end;
  4947. OP_EEND:
  4948. begin
  4949. Result := True; // Success!
  4950. Exit;
  4951. end;
  4952. {$IFDEF FastUnicodeData}
  4953. OP_ANYCATEGORY:
  4954. begin
  4955. if (regInput >= fInputEnd) then Exit;
  4956. if not MatchOneCharCategory(scan + REOpSz + RENextOffSz, regInput) then Exit;
  4957. {$IFDEF UNICODEEX}
  4958. IncUnicode(regInput);
  4959. {$ELSE}
  4960. Inc(regInput);
  4961. {$ENDIF}
  4962. end;
  4963. OP_NOTCATEGORY:
  4964. begin
  4965. if (regInput >= fInputEnd) then Exit;
  4966. if MatchOneCharCategory(scan + REOpSz + RENextOffSz, regInput) then Exit;
  4967. {$IFDEF UNICODEEX}
  4968. IncUnicode(regInput);
  4969. {$ELSE}
  4970. Inc(regInput);
  4971. {$ENDIF}
  4972. end;
  4973. {$ENDIF}
  4974. OP_RECUR:
  4975. begin
  4976. // call opcode start
  4977. if regRecursion < RegexMaxRecursion then
  4978. begin
  4979. Inc(regRecursion);
  4980. bound1 := MatchPrim(regCodeWork);
  4981. Dec(regRecursion);
  4982. end
  4983. else
  4984. bound1 := False;
  4985. if not bound1 then Exit;
  4986. end;
  4987. OP_SUBCALL_FIRST .. OP_SUBCALL_LAST:
  4988. begin
  4989. // call subroutine
  4990. no := GrpIndexes[Ord(scan^) - Ord(OP_SUBCALL)];
  4991. if no < 0 then Exit;
  4992. save := GrpOpCodes[no];
  4993. if save = nil then Exit;
  4994. if regRecursion < RegexMaxRecursion then
  4995. begin
  4996. // mark group in GrpSubCalled array so opcode can detect subcall
  4997. checkAtomicGroup := GrpSubCalled[no];
  4998. GrpSubCalled[no] := True;
  4999. Inc(regRecursion);
  5000. bound1 := MatchPrim(save);
  5001. Dec(regRecursion);
  5002. GrpSubCalled[no] := checkAtomicGroup;
  5003. end
  5004. else
  5005. bound1 := False;
  5006. if not bound1 then Exit;
  5007. end;
  5008. else
  5009. begin
  5010. Error(reeMatchPrimMemoryCorruption);
  5011. Exit;
  5012. end;
  5013. end; { of case scan^ }
  5014. scan := next;
  5015. end; { of while scan <> nil }
  5016. // We get here only if there's trouble -- normally "case EEND" is the
  5017. // terminating point.
  5018. Error(reeMatchPrimCorruptedPointers);
  5019. end; { of function TRegExpr.MatchPrim
  5020. -------------------------------------------------------------- }
  5021. function TRegExpr.Exec(const AInputString: RegExprString): boolean;
  5022. begin
  5023. InputString := AInputString;
  5024. Result := ExecPrim(1, False, False, False);
  5025. end; { of function TRegExpr.Exec
  5026. -------------------------------------------------------------- }
  5027. {$IFDEF OverMeth}
  5028. function TRegExpr.Exec: boolean;
  5029. var
  5030. SlowChecks: boolean;
  5031. begin
  5032. SlowChecks := Length(fInputString) < fSlowChecksSizeMax;
  5033. Result := ExecPrim(1, False, SlowChecks, False);
  5034. end; { of function TRegExpr.Exec
  5035. -------------------------------------------------------------- }
  5036. function TRegExpr.Exec(AOffset: integer): boolean;
  5037. begin
  5038. Result := ExecPrim(AOffset, False, False, False);
  5039. end; { of function TRegExpr.Exec
  5040. -------------------------------------------------------------- }
  5041. {$ENDIF}
  5042. function TRegExpr.ExecPos(AOffset: integer {$IFDEF DefParam} = 1{$ENDIF}): boolean;
  5043. begin
  5044. Result := ExecPrim(AOffset, False, False, False);
  5045. end; { of function TRegExpr.ExecPos
  5046. -------------------------------------------------------------- }
  5047. {$IFDEF OverMeth}
  5048. function TRegExpr.ExecPos(AOffset: integer; ATryOnce, ABackward: boolean): boolean;
  5049. begin
  5050. Result := ExecPrim(AOffset, ATryOnce, False, ABackward);
  5051. end;
  5052. {$ENDIF}
  5053. function TRegExpr.MatchAtOnePos(APos: PRegExprChar): boolean;
  5054. begin
  5055. // test for lookbehind '(?<!foo)bar' before running actual MatchPrim
  5056. if Assigned(fHelper) then
  5057. if (APos - fHelperLen) >= fInputStart then
  5058. begin
  5059. fHelper.SetInputRange(APos - fHelperLen, APos);
  5060. if fHelper.MatchAtOnePos(APos - fHelperLen) then
  5061. begin
  5062. Result := False;
  5063. Exit;
  5064. end;
  5065. end;
  5066. regInput := APos;
  5067. regCurrentGrp := -1;
  5068. regNestedCalls := 0;
  5069. regRecursion := 0;
  5070. Result := MatchPrim(regCodeWork);
  5071. if Result then
  5072. begin
  5073. GrpBounds[0].GrpStart[0] := APos;
  5074. GrpBounds[0].GrpEnd[0] := regInput;
  5075. // with lookbehind, increase found position by the len of group=1
  5076. if regLookbehind then
  5077. Inc(GrpBounds[0].GrpStart[0], GrpBounds[0].GrpEnd[1] - GrpBounds[0].GrpStart[1]);
  5078. // with lookahead, decrease ending by the len of group=regLookaheadGroup
  5079. if regLookahead and (regLookaheadGroup > 0) then
  5080. Dec(GrpBounds[0].GrpEnd[0], GrpBounds[0].GrpEnd[regLookaheadGroup] - GrpBounds[0].GrpStart[regLookaheadGroup]);
  5081. end;
  5082. end;
  5083. procedure TRegExpr.ClearMatches;
  5084. begin
  5085. FillChar(GrpBounds, SizeOf(GrpBounds), 0);
  5086. FillChar(GrpAtomicDone, SizeOf(GrpAtomicDone), 0);
  5087. FillChar(GrpSubCalled, SizeOf(GrpSubCalled), 0);
  5088. end;
  5089. procedure TRegExpr.ClearInternalIndexes;
  5090. var
  5091. i: integer;
  5092. begin
  5093. FillChar(GrpBounds, SizeOf(GrpBounds), 0);
  5094. FillChar(GrpAtomic, SizeOf(GrpAtomic), 0);
  5095. FillChar(GrpAtomicDone, SizeOf(GrpAtomicDone), 0);
  5096. FillChar(GrpSubCalled, SizeOf(GrpSubCalled), 0);
  5097. FillChar(GrpOpCodes, SizeOf(GrpOpCodes), 0);
  5098. for i := 0 to RegexMaxGroups - 1 do
  5099. begin
  5100. GrpIndexes[i] := -1;
  5101. GrpNames[i] := '';
  5102. end;
  5103. GrpIndexes[0] := 0;
  5104. GrpCount := 0;
  5105. end;
  5106. function TRegExpr.ExecPrim(AOffset: integer;
  5107. ATryOnce, ASlowChecks, ABackward: boolean): boolean;
  5108. var
  5109. Ptr: PRegExprChar;
  5110. begin
  5111. Result := False;
  5112. // Ensure that Match cleared either if optimization tricks or some error
  5113. // will lead to leaving ExecPrim without actual search. That is
  5114. // important for ExecNext logic and so on.
  5115. ClearMatches;
  5116. // Don't check IsProgrammOk here! it causes big slowdown in test_benchmark!
  5117. if programm = nil then
  5118. begin
  5119. Compile;
  5120. if programm = nil then
  5121. Exit;
  5122. end;
  5123. if fInputString = '' then
  5124. begin
  5125. // Empty string can match e.g. '^$'
  5126. if regMustLen > 0 then
  5127. Exit;
  5128. end;
  5129. // Check that the start position is not negative
  5130. if AOffset < 1 then
  5131. begin
  5132. Error(reeOffsetMustBePositive);
  5133. Exit;
  5134. end;
  5135. // Check that the start position is not longer than the line
  5136. if AOffset > (Length(fInputString) + 1) then
  5137. Exit;
  5138. Ptr := fInputStart + AOffset - 1;
  5139. // If there is a "must appear" string, look for it.
  5140. if ASlowChecks then
  5141. if regMustString <> '' then
  5142. if Pos(regMustString, fInputString) = 0 then Exit;
  5143. {$IFDEF ComplexBraces}
  5144. // no loops started
  5145. LoopStackIdx := 0; // ###0.925
  5146. {$ENDIF}
  5147. // ATryOnce or anchored match (it needs to be tried only once).
  5148. if ATryOnce or (regAnchored <> #0) then
  5149. begin
  5150. {$IFDEF UseFirstCharSet}
  5151. {$IFDEF UnicodeRE}
  5152. if Ord(Ptr^) <= $FF then
  5153. {$ENDIF}
  5154. if not FirstCharArray[byte(Ptr^)] then
  5155. Exit;
  5156. {$ENDIF}
  5157. Result := MatchAtOnePos(Ptr);
  5158. Exit;
  5159. end;
  5160. // Messy cases: unanchored match.
  5161. if ABackward then
  5162. Inc(Ptr, 2)
  5163. else
  5164. Dec(Ptr);
  5165. repeat
  5166. if ABackward then
  5167. begin
  5168. Dec(Ptr);
  5169. if Ptr < fInputStart then
  5170. Exit;
  5171. end
  5172. else
  5173. begin
  5174. Inc(Ptr);
  5175. if Ptr > fInputEnd then
  5176. Exit;
  5177. end;
  5178. {$IFDEF UseFirstCharSet}
  5179. {$IFDEF UnicodeRE}
  5180. if Ord(Ptr^) <= $FF then
  5181. {$ENDIF}
  5182. if not FirstCharArray[byte(Ptr^)] then
  5183. Continue;
  5184. {$ENDIF}
  5185. Result := MatchAtOnePos(Ptr);
  5186. // Exit on a match or after testing the end-of-string
  5187. if Result then
  5188. Exit;
  5189. until False;
  5190. end; { of function TRegExpr.ExecPrim
  5191. -------------------------------------------------------------- }
  5192. function TRegExpr.ExecNext(ABackward: boolean {$IFDEF DefParam} = False{$ENDIF}): boolean;
  5193. var
  5194. PtrBegin, PtrEnd: PRegExprChar;
  5195. Offset: PtrInt;
  5196. begin
  5197. PtrBegin := GrpBounds[0].GrpStart[0];
  5198. PtrEnd := GrpBounds[0].GrpEnd[0];
  5199. if (PtrBegin = nil) or (PtrEnd = nil) then
  5200. begin
  5201. Error(reeExecNextWithoutExec);
  5202. Result := False;
  5203. Exit;
  5204. end;
  5205. Offset := PtrEnd - fInputStart + 1;
  5206. // prevent infinite looping if empty string matches r.e.
  5207. if PtrBegin = PtrEnd then
  5208. Inc(Offset);
  5209. Result := ExecPrim(Offset, False, False, ABackward);
  5210. end; { of function TRegExpr.ExecNext
  5211. -------------------------------------------------------------- }
  5212. procedure TRegExpr.SetInputString(const AInputString: RegExprString);
  5213. begin
  5214. ClearMatches;
  5215. fInputString := AInputString;
  5216. UniqueString(fInputString);
  5217. fInputStart := PRegExprChar(fInputString);
  5218. fInputEnd := fInputStart + Length(fInputString);
  5219. end;
  5220. procedure TRegExpr.SetInputRange(AStart, AEnd: PRegExprChar);
  5221. begin
  5222. fInputString := '';
  5223. fInputStart := AStart;
  5224. fInputEnd := AEnd;
  5225. end;
  5226. {$IFDEF UseLineSep}
  5227. procedure TRegExpr.SetLineSeparators(const AStr: RegExprString);
  5228. begin
  5229. if AStr <> fLineSeparators then
  5230. begin
  5231. fLineSeparators := AStr;
  5232. InitLineSepArray;
  5233. InvalidateProgramm;
  5234. end;
  5235. end; { of procedure TRegExpr.SetLineSeparators
  5236. -------------------------------------------------------------- }
  5237. {$ENDIF}
  5238. procedure TRegExpr.SetUsePairedBreak(AValue: boolean);
  5239. begin
  5240. if AValue <> fUsePairedBreak then
  5241. begin
  5242. fUsePairedBreak := AValue;
  5243. InvalidateProgramm;
  5244. end;
  5245. end;
  5246. function TRegExpr.Substitute(const ATemplate: RegExprString): RegExprString;
  5247. // perform substitutions after a regexp match
  5248. var
  5249. TemplateBeg, TemplateEnd: PRegExprChar;
  5250. function ParseVarName(var APtr: PRegExprChar): integer;
  5251. // extract name of variable: $1 or ${1} or ${name}
  5252. // from APtr^, uses TemplateEnd
  5253. var
  5254. p: PRegExprChar;
  5255. Delimited: boolean;
  5256. GrpName: RegExprString;
  5257. begin
  5258. Result := 0;
  5259. GrpName := '';
  5260. p := APtr;
  5261. Delimited := (p < TemplateEnd) and (p^ = '{');
  5262. if Delimited then
  5263. Inc(p); // skip left curly brace
  5264. if (p < TemplateEnd) and (p^ = '&') then
  5265. Inc(p) // this is '$&' or '${&}'
  5266. else
  5267. begin
  5268. if IsDigitChar(p^) then
  5269. begin
  5270. while (p < TemplateEnd) and IsDigitChar(p^) do
  5271. begin
  5272. Result := Result * 10 + (Ord(p^) - Ord('0'));
  5273. Inc(p);
  5274. end
  5275. end
  5276. else
  5277. if Delimited then
  5278. begin
  5279. FindGroupName(p, TemplateEnd, '}', GrpName);
  5280. Result := MatchIndexFromName(GrpName);
  5281. Inc(p, Length(GrpName));
  5282. end;
  5283. end;
  5284. if Delimited then
  5285. if (p < TemplateEnd) and (p^ = '}') then
  5286. Inc(p) // skip right curly brace
  5287. else
  5288. p := APtr; // isn't properly terminated
  5289. if p = APtr then
  5290. Result := -1; // no valid digits found or no right curly brace
  5291. APtr := p;
  5292. end;
  5293. procedure FindSubstGroupIndex(var p: PRegExprChar; var Idx: integer; var NumberFound: boolean);
  5294. begin
  5295. Idx := ParseVarName(p);
  5296. NumberFound := (Idx >= 0) and (Idx <= High(GrpIndexes));
  5297. if NumberFound then
  5298. Idx := GrpIndexes[Idx];
  5299. end;
  5300. type
  5301. TSubstMode = (smodeNormal, smodeOneUpper, smodeOneLower, smodeAllUpper, smodeAllLower);
  5302. var
  5303. Mode: TSubstMode;
  5304. p, p0, p1, ResultPtr: PRegExprChar;
  5305. ResultLen, n: integer;
  5306. Ch, QuotedChar: REChar;
  5307. GroupFound: boolean;
  5308. begin
  5309. // Check programm and input string
  5310. if not IsProgrammOk then
  5311. Exit;
  5312. // Note: don't check for empty fInputString, it's valid case,
  5313. // e.g. user needs to replace regex "\b" to "_", it's zero match length
  5314. if ATemplate = '' then
  5315. begin
  5316. Result := '';
  5317. Exit;
  5318. end;
  5319. TemplateBeg := PRegExprChar(ATemplate);
  5320. TemplateEnd := TemplateBeg + Length(ATemplate);
  5321. // Count result length for speed optimization.
  5322. ResultLen := 0;
  5323. p := TemplateBeg;
  5324. while p < TemplateEnd do
  5325. begin
  5326. Ch := p^;
  5327. Inc(p);
  5328. n := -1;
  5329. GroupFound := False;
  5330. if Ch = SubstituteGroupChar then
  5331. FindSubstGroupIndex(p, n, GroupFound);
  5332. if GroupFound then
  5333. begin
  5334. if n >= 0 then
  5335. Inc(ResultLen, GrpBounds[0].GrpEnd[n] - GrpBounds[0].GrpStart[n]);
  5336. end
  5337. else
  5338. begin
  5339. if (Ch = EscChar) and (p < TemplateEnd) then
  5340. begin // quoted or special AnsiChar followed
  5341. Ch := p^;
  5342. Inc(p);
  5343. case Ch of
  5344. 'n':
  5345. Inc(ResultLen, Length(fReplaceLineEnd));
  5346. 'u', 'l', 'U', 'L': { nothing }
  5347. ;
  5348. 'x':
  5349. begin
  5350. Inc(ResultLen);
  5351. if (p^ = '{') then
  5352. begin // skip \x{....}
  5353. while ((p^ <> '}') and (p < TemplateEnd)) do
  5354. p := p + 1;
  5355. p := p + 1;
  5356. end
  5357. else
  5358. p := p + 2 // skip \x..
  5359. end;
  5360. else
  5361. Inc(ResultLen);
  5362. end;
  5363. end
  5364. else
  5365. Inc(ResultLen);
  5366. end;
  5367. end;
  5368. // Get memory. We do it once and it significant speed up work !
  5369. if ResultLen = 0 then
  5370. begin
  5371. Result := '';
  5372. Exit;
  5373. end;
  5374. SetLength(Result, ResultLen);
  5375. // Fill Result
  5376. ResultPtr := PRegExprChar(Result);
  5377. p := TemplateBeg;
  5378. Mode := smodeNormal;
  5379. while p < TemplateEnd do
  5380. begin
  5381. Ch := p^;
  5382. p0 := p;
  5383. Inc(p);
  5384. p1 := p;
  5385. n := -1;
  5386. GroupFound := False;
  5387. if Ch = SubstituteGroupChar then
  5388. FindSubstGroupIndex(p, n, GroupFound);
  5389. if GroupFound then
  5390. begin
  5391. if n >= 0 then
  5392. begin
  5393. p0 := GrpBounds[0].GrpStart[n];
  5394. p1 := GrpBounds[0].GrpEnd[n];
  5395. end
  5396. else
  5397. p1 := p0;
  5398. end
  5399. else
  5400. begin
  5401. if (Ch = EscChar) and (p < TemplateEnd) then
  5402. begin // quoted or special AnsiChar followed
  5403. Ch := p^;
  5404. Inc(p);
  5405. case Ch of
  5406. 'n':
  5407. begin
  5408. p0 := PRegExprChar(fReplaceLineEnd);
  5409. p1 := p0 + Length(fReplaceLineEnd);
  5410. end;
  5411. 'x', 't', 'r', 'f', 'a', 'e':
  5412. begin
  5413. p := p - 1;
  5414. // UnquoteChar expects the escaped AnsiChar under the pointer
  5415. QuotedChar := UnQuoteChar(p, TemplateEnd);
  5416. p := p + 1;
  5417. // Skip after last part of the escaped sequence - UnquoteChar stops on the last symbol of it
  5418. p0 := @QuotedChar;
  5419. p1 := p0 + 1;
  5420. end;
  5421. 'l':
  5422. begin
  5423. Mode := smodeOneLower;
  5424. p1 := p0;
  5425. end;
  5426. 'L':
  5427. begin
  5428. Mode := smodeAllLower;
  5429. p1 := p0;
  5430. end;
  5431. 'u':
  5432. begin
  5433. Mode := smodeOneUpper;
  5434. p1 := p0;
  5435. end;
  5436. 'U':
  5437. begin
  5438. Mode := smodeAllUpper;
  5439. p1 := p0;
  5440. end;
  5441. else
  5442. begin
  5443. Inc(p0);
  5444. Inc(p1);
  5445. end;
  5446. end;
  5447. end
  5448. end;
  5449. if p0 < p1 then
  5450. begin
  5451. while p0 < p1 do
  5452. begin
  5453. case Mode of
  5454. smodeOneLower:
  5455. begin
  5456. ResultPtr^ := _LowerCase(p0^);
  5457. Mode := smodeNormal;
  5458. end;
  5459. smodeAllLower:
  5460. begin
  5461. ResultPtr^ := _LowerCase(p0^);
  5462. end;
  5463. smodeOneUpper:
  5464. begin
  5465. ResultPtr^ := _UpperCase(p0^);
  5466. Mode := smodeNormal;
  5467. end;
  5468. smodeAllUpper:
  5469. begin
  5470. ResultPtr^ := _UpperCase(p0^);
  5471. end;
  5472. else
  5473. ResultPtr^ := p0^;
  5474. end;
  5475. Inc(ResultPtr);
  5476. Inc(p0);
  5477. end;
  5478. Mode := smodeNormal;
  5479. end;
  5480. end;
  5481. end; { of function TRegExpr.Substitute
  5482. -------------------------------------------------------------- }
  5483. procedure TRegExpr.Split(const AInputStr: RegExprString; APieces: TStrings);
  5484. var
  5485. PrevPos: PtrInt;
  5486. begin
  5487. PrevPos := 1;
  5488. if Exec(AInputStr) then
  5489. repeat
  5490. APieces.Add(System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos));
  5491. PrevPos := MatchPos[0] + MatchLen[0];
  5492. until not ExecNext;
  5493. APieces.Add(System.Copy(AInputStr, PrevPos, MaxInt)); // Tail
  5494. end; { of procedure TRegExpr.Split
  5495. -------------------------------------------------------------- }
  5496. function TRegExpr.Replace(const AInputStr: RegExprString;
  5497. const AReplaceStr: RegExprString;
  5498. AUseSubstitution: boolean{$IFDEF DefParam} = False{$ENDIF}): RegExprString;
  5499. var
  5500. PrevPos: PtrInt;
  5501. begin
  5502. Result := '';
  5503. PrevPos := 1;
  5504. if Exec(AInputStr) then
  5505. repeat
  5506. Result := Result + System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos);
  5507. if AUseSubstitution // ###0.946
  5508. then
  5509. Result := Result + Substitute(AReplaceStr)
  5510. else
  5511. Result := Result + AReplaceStr;
  5512. PrevPos := MatchPos[0] + MatchLen[0];
  5513. until not ExecNext;
  5514. Result := Result + System.Copy(AInputStr, PrevPos, MaxInt); // Tail
  5515. end; { of function TRegExpr.Replace
  5516. -------------------------------------------------------------- }
  5517. function TRegExpr.ReplaceEx(const AInputStr: RegExprString;
  5518. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  5519. var
  5520. PrevPos: PtrInt;
  5521. begin
  5522. Result := '';
  5523. PrevPos := 1;
  5524. if Exec(AInputStr) then
  5525. repeat
  5526. Result := Result + System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos)
  5527. + AReplaceFunc(Self);
  5528. PrevPos := MatchPos[0] + MatchLen[0];
  5529. until not ExecNext;
  5530. Result := Result + System.Copy(AInputStr, PrevPos, MaxInt); // Tail
  5531. end; { of function TRegExpr.ReplaceEx
  5532. -------------------------------------------------------------- }
  5533. {$IFDEF OverMeth}
  5534. function TRegExpr.Replace(const AInputStr: RegExprString;
  5535. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  5536. begin
  5537. Result := ReplaceEx(AInputStr, AReplaceFunc);
  5538. end; { of function TRegExpr.Replace
  5539. -------------------------------------------------------------- }
  5540. {$ENDIF}
  5541. { ============================================================= }
  5542. { ====================== Debug section ======================== }
  5543. { ============================================================= }
  5544. {$IFDEF UseFirstCharSet}
  5545. procedure TRegExpr.FillFirstCharSet(prog: PRegExprChar);
  5546. var
  5547. scan: PRegExprChar; // Current node.
  5548. Next: PRegExprChar; // Next node.
  5549. opnd: PRegExprChar;
  5550. Oper: TREOp;
  5551. ch: REChar;
  5552. min_cnt: integer;
  5553. {$IFDEF UseLineSep}
  5554. i: integer;
  5555. {$ENDIF}
  5556. TempSet: TRegExprCharset;
  5557. begin
  5558. TempSet := [];
  5559. scan := prog;
  5560. while scan <> nil do
  5561. begin
  5562. Next := regNext(scan);
  5563. Oper := PREOp(scan)^;
  5564. case Oper of
  5565. OP_BSUBEXP,
  5566. OP_BSUBEXPCI:
  5567. begin
  5568. // we cannot optimize r.e. if it starts with back reference
  5569. FirstCharSet := RegExprAllSet; //###0.930
  5570. Exit;
  5571. end;
  5572. OP_BOL,
  5573. OP_BOLML:
  5574. ; // Exit; //###0.937
  5575. OP_EOL,
  5576. OP_EOL2,
  5577. OP_EOLML:
  5578. begin //###0.948 was empty in 0.947, was EXIT in 0.937
  5579. Include(FirstCharSet, 0);
  5580. if ModifierM then
  5581. begin
  5582. {$IFDEF UseLineSep}
  5583. for i := 1 to Length(LineSeparators) do
  5584. Include(FirstCharSet, byte(LineSeparators[i]));
  5585. {$ELSE}
  5586. FirstCharSet := FirstCharSet + RegExprLineSeparatorsSet;
  5587. {$ENDIF}
  5588. end;
  5589. Exit;
  5590. end;
  5591. OP_BOUND,
  5592. OP_NOTBOUND:
  5593. ; //###0.943 ?!!
  5594. OP_ANY,
  5595. OP_ANYML:
  5596. begin // we can better define ANYML !!!
  5597. FirstCharSet := RegExprAllSet; //###0.930
  5598. Exit;
  5599. end;
  5600. OP_ANYDIGIT:
  5601. begin
  5602. FirstCharSet := FirstCharSet + RegExprDigitSet;
  5603. Exit;
  5604. end;
  5605. OP_NOTDIGIT:
  5606. begin
  5607. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprDigitSet);
  5608. Exit;
  5609. end;
  5610. OP_ANYLETTER:
  5611. begin
  5612. GetCharSetFromWordChars(TempSet);
  5613. FirstCharSet := FirstCharSet + TempSet;
  5614. Exit;
  5615. end;
  5616. OP_NOTLETTER:
  5617. begin
  5618. GetCharSetFromWordChars(TempSet);
  5619. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  5620. Exit;
  5621. end;
  5622. OP_ANYSPACE:
  5623. begin
  5624. GetCharSetFromSpaceChars(TempSet);
  5625. FirstCharSet := FirstCharSet + TempSet;
  5626. Exit;
  5627. end;
  5628. OP_NOTSPACE:
  5629. begin
  5630. GetCharSetFromSpaceChars(TempSet);
  5631. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  5632. Exit;
  5633. end;
  5634. OP_ANYVERTSEP:
  5635. begin
  5636. FirstCharSet := FirstCharSet + RegExprLineSeparatorsSet;
  5637. Exit;
  5638. end;
  5639. OP_NOTVERTSEP:
  5640. begin
  5641. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprLineSeparatorsSet);
  5642. Exit;
  5643. end;
  5644. OP_ANYHORZSEP:
  5645. begin
  5646. FirstCharSet := FirstCharSet + RegExprHorzSeparatorsSet;
  5647. Exit;
  5648. end;
  5649. OP_NOTHORZSEP:
  5650. begin
  5651. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprHorzSeparatorsSet);
  5652. Exit;
  5653. end;
  5654. OP_EXACTLYCI:
  5655. begin
  5656. ch := (scan + REOpSz + RENextOffSz + RENumberSz)^;
  5657. {$IFDEF UnicodeRE}
  5658. if Ord(ch) <= $FF then
  5659. {$ENDIF}
  5660. begin
  5661. Include(FirstCharSet, byte(ch));
  5662. Include(FirstCharSet, byte(InvertCase(ch)));
  5663. end;
  5664. Exit;
  5665. end;
  5666. OP_EXACTLY:
  5667. begin
  5668. ch := (scan + REOpSz + RENextOffSz + RENumberSz)^;
  5669. {$IFDEF UnicodeRE}
  5670. if Ord(ch) <= $FF then
  5671. {$ENDIF}
  5672. Include(FirstCharSet, byte(ch));
  5673. Exit;
  5674. end;
  5675. OP_ANYOF:
  5676. begin
  5677. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, False, TempSet);
  5678. FirstCharSet := FirstCharSet + TempSet;
  5679. Exit;
  5680. end;
  5681. OP_ANYBUT:
  5682. begin
  5683. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, False, TempSet);
  5684. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  5685. Exit;
  5686. end;
  5687. OP_ANYOFCI:
  5688. begin
  5689. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, True, TempSet);
  5690. FirstCharSet := FirstCharSet + TempSet;
  5691. Exit;
  5692. end;
  5693. OP_ANYBUTCI:
  5694. begin
  5695. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, True, TempSet);
  5696. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  5697. Exit;
  5698. end;
  5699. OP_NOTHING:
  5700. ;
  5701. OP_COMMENT:
  5702. ;
  5703. OP_BACK:
  5704. ;
  5705. OP_OPEN_FIRST .. OP_OPEN_LAST:
  5706. begin
  5707. FillFirstCharSet(Next);
  5708. Exit;
  5709. end;
  5710. OP_CLOSE_FIRST .. OP_CLOSE_LAST:
  5711. begin
  5712. FillFirstCharSet(Next);
  5713. Exit;
  5714. end;
  5715. OP_BRANCH:
  5716. begin
  5717. if (PREOp(Next)^ <> OP_BRANCH) // No choice.
  5718. then
  5719. Next := scan + REOpSz + RENextOffSz // Avoid recursion.
  5720. else
  5721. begin
  5722. repeat
  5723. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  5724. scan := regNext(scan);
  5725. until (scan = nil) or (PREOp(scan)^ <> OP_BRANCH);
  5726. Exit;
  5727. end;
  5728. end;
  5729. {$IFDEF ComplexBraces}
  5730. OP_LOOPENTRY:
  5731. begin //###0.925
  5732. //LoopStack [LoopStackIdx] := 0; //###0.940 line removed
  5733. FillFirstCharSet(Next); // execute LOOP
  5734. Exit;
  5735. end;
  5736. OP_LOOP,
  5737. OP_LOOPNG:
  5738. begin //###0.940
  5739. opnd := scan + PRENextOff(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz * 2))^;
  5740. min_cnt := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  5741. FillFirstCharSet(opnd);
  5742. if min_cnt = 0 then
  5743. FillFirstCharSet(Next);
  5744. Exit;
  5745. end;
  5746. {$ENDIF}
  5747. OP_STAR,
  5748. OP_STARNG,
  5749. OP_STAR_POSS: //###0.940
  5750. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  5751. OP_PLUS,
  5752. OP_PLUSNG,
  5753. OP_PLUS_POSS:
  5754. begin //###0.940
  5755. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  5756. Exit;
  5757. end;
  5758. OP_BRACES,
  5759. OP_BRACESNG,
  5760. OP_BRACES_POSS:
  5761. begin //###0.940
  5762. opnd := scan + REOpSz + RENextOffSz + REBracesArgSz * 2;
  5763. min_cnt := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^; // BRACES
  5764. FillFirstCharSet(opnd);
  5765. if min_cnt > 0 then
  5766. Exit;
  5767. end;
  5768. OP_EEND:
  5769. begin
  5770. FirstCharSet := RegExprAllSet; //###0.948
  5771. Exit;
  5772. end;
  5773. OP_ANYCATEGORY,
  5774. OP_NOTCATEGORY:
  5775. begin
  5776. FirstCharSet := RegExprAllSet;
  5777. Exit;
  5778. end;
  5779. OP_RECUR,
  5780. OP_SUBCALL_FIRST .. OP_SUBCALL_LAST:
  5781. begin
  5782. end
  5783. else
  5784. begin
  5785. fLastErrorOpcode := Oper;
  5786. Error(reeUnknownOpcodeInFillFirst);
  5787. Exit;
  5788. end;
  5789. end; { of case scan^}
  5790. scan := Next;
  5791. end; { of while scan <> nil}
  5792. end; { of procedure FillFirstCharSet
  5793. --------------------------------------------------------------}
  5794. {$ENDIF}
  5795. procedure TRegExpr.InitCharCheckers;
  5796. var
  5797. Cnt: integer;
  5798. //
  5799. function Add(AChecker: TRegExprCharChecker): byte;
  5800. begin
  5801. Inc(Cnt);
  5802. if Cnt > High(CharCheckers) then
  5803. Error(reeTooSmallCheckersArray);
  5804. CharCheckers[Cnt - 1] := AChecker;
  5805. Result := Cnt - 1;
  5806. end;
  5807. //
  5808. begin
  5809. Cnt := 0;
  5810. FillChar(CharCheckers, SizeOf(CharCheckers), 0);
  5811. CheckerIndex_Word := Add(CharChecker_Word);
  5812. CheckerIndex_NotWord := Add(CharChecker_NotWord);
  5813. CheckerIndex_Space := Add(CharChecker_Space);
  5814. CheckerIndex_NotSpace := Add(CharChecker_NotSpace);
  5815. CheckerIndex_Digit := Add(CharChecker_Digit);
  5816. CheckerIndex_NotDigit := Add(CharChecker_NotDigit);
  5817. CheckerIndex_VertSep := Add(CharChecker_VertSep);
  5818. CheckerIndex_NotVertSep := Add(CharChecker_NotVertSep);
  5819. CheckerIndex_HorzSep := Add(CharChecker_HorzSep);
  5820. CheckerIndex_NotHorzSep := Add(CharChecker_NotHorzSep);
  5821. //CheckerIndex_AllAZ := Add(CharChecker_AllAZ);
  5822. CheckerIndex_LowerAZ := Add(CharChecker_LowerAZ);
  5823. CheckerIndex_UpperAZ := Add(CharChecker_UpperAZ);
  5824. SetLength(CharCheckerInfos, 3);
  5825. with CharCheckerInfos[0] do
  5826. begin
  5827. CharBegin := 'a';
  5828. CharEnd:= 'z';
  5829. CheckerIndex := CheckerIndex_LowerAZ;
  5830. end;
  5831. with CharCheckerInfos[1] do
  5832. begin
  5833. CharBegin := 'A';
  5834. CharEnd := 'Z';
  5835. CheckerIndex := CheckerIndex_UpperAZ;
  5836. end;
  5837. with CharCheckerInfos[2] do
  5838. begin
  5839. CharBegin := '0';
  5840. CharEnd := '9';
  5841. CheckerIndex := CheckerIndex_Digit;
  5842. end;
  5843. end;
  5844. function TRegExpr.CharChecker_Word(ch: REChar): boolean;
  5845. begin
  5846. Result := IsWordChar(ch);
  5847. end;
  5848. function TRegExpr.CharChecker_NotWord(ch: REChar): boolean;
  5849. begin
  5850. Result := not IsWordChar(ch);
  5851. end;
  5852. function TRegExpr.CharChecker_Space(ch: REChar): boolean;
  5853. begin
  5854. Result := IsSpaceChar(ch);
  5855. end;
  5856. function TRegExpr.CharChecker_NotSpace(ch: REChar): boolean;
  5857. begin
  5858. Result := not IsSpaceChar(ch);
  5859. end;
  5860. function TRegExpr.CharChecker_Digit(ch: REChar): boolean;
  5861. begin
  5862. Result := IsDigitChar(ch);
  5863. end;
  5864. function TRegExpr.CharChecker_NotDigit(ch: REChar): boolean;
  5865. begin
  5866. Result := not IsDigitChar(ch);
  5867. end;
  5868. function TRegExpr.CharChecker_VertSep(ch: REChar): boolean;
  5869. begin
  5870. Result := IsVertLineSeparator(ch);
  5871. end;
  5872. function TRegExpr.CharChecker_NotVertSep(ch: REChar): boolean;
  5873. begin
  5874. Result := not IsVertLineSeparator(ch);
  5875. end;
  5876. function TRegExpr.CharChecker_HorzSep(ch: REChar): boolean;
  5877. begin
  5878. Result := IsHorzSeparator(ch);
  5879. end;
  5880. function TRegExpr.CharChecker_NotHorzSep(ch: REChar): boolean;
  5881. begin
  5882. Result := not IsHorzSeparator(ch);
  5883. end;
  5884. function TRegExpr.CharChecker_LowerAZ(ch: REChar): boolean;
  5885. begin
  5886. case ch of
  5887. 'a' .. 'z':
  5888. Result := True;
  5889. else
  5890. Result := False;
  5891. end;
  5892. end;
  5893. function TRegExpr.CharChecker_UpperAZ(ch: REChar): boolean;
  5894. begin
  5895. case ch of
  5896. 'A' .. 'Z':
  5897. Result := True;
  5898. else
  5899. Result := False;
  5900. end;
  5901. end;
  5902. {$IFDEF RegExpPCodeDump}
  5903. function TRegExpr.DumpOp(op: TREOp): RegExprString;
  5904. // printable representation of opcode
  5905. begin
  5906. case op of
  5907. OP_BOL:
  5908. Result := 'BOL';
  5909. OP_EOL:
  5910. Result := 'EOL';
  5911. OP_EOL2:
  5912. Result := 'EOL2';
  5913. OP_BOLML:
  5914. Result := 'BOLML';
  5915. OP_EOLML:
  5916. Result := 'EOLML';
  5917. OP_BOUND:
  5918. Result := 'BOUND';
  5919. OP_NOTBOUND:
  5920. Result := 'NOTBOUND';
  5921. OP_ANY:
  5922. Result := 'ANY';
  5923. OP_ANYML:
  5924. Result := 'ANYML';
  5925. OP_ANYLETTER:
  5926. Result := 'ANYLETTER';
  5927. OP_NOTLETTER:
  5928. Result := 'NOTLETTER';
  5929. OP_ANYDIGIT:
  5930. Result := 'ANYDIGIT';
  5931. OP_NOTDIGIT:
  5932. Result := 'NOTDIGIT';
  5933. OP_ANYSPACE:
  5934. Result := 'ANYSPACE';
  5935. OP_NOTSPACE:
  5936. Result := 'NOTSPACE';
  5937. OP_ANYHORZSEP:
  5938. Result := 'ANYHORZSEP';
  5939. OP_NOTHORZSEP:
  5940. Result := 'NOTHORZSEP';
  5941. OP_ANYVERTSEP:
  5942. Result := 'ANYVERTSEP';
  5943. OP_NOTVERTSEP:
  5944. Result := 'NOTVERTSEP';
  5945. OP_ANYOF:
  5946. Result := 'ANYOF';
  5947. OP_ANYBUT:
  5948. Result := 'ANYBUT';
  5949. OP_ANYOFCI:
  5950. Result := 'ANYOF/CI';
  5951. OP_ANYBUTCI:
  5952. Result := 'ANYBUT/CI';
  5953. OP_BRANCH:
  5954. Result := 'BRANCH';
  5955. OP_EXACTLY:
  5956. Result := 'EXACTLY';
  5957. OP_EXACTLYCI:
  5958. Result := 'EXACTLY/CI';
  5959. OP_NOTHING:
  5960. Result := 'NOTHING';
  5961. OP_COMMENT:
  5962. Result := 'COMMENT';
  5963. OP_BACK:
  5964. Result := 'BACK';
  5965. OP_EEND:
  5966. Result := 'END';
  5967. OP_BSUBEXP:
  5968. Result := 'BSUBEXP';
  5969. OP_BSUBEXPCI:
  5970. Result := 'BSUBEXP/CI';
  5971. OP_OPEN_FIRST .. OP_OPEN_LAST:
  5972. Result := Format('OPEN[%d]', [Ord(op) - Ord(OP_OPEN)]);
  5973. OP_CLOSE_FIRST .. OP_CLOSE_LAST:
  5974. Result := Format('CLOSE[%d]', [Ord(op) - Ord(OP_CLOSE)]);
  5975. OP_STAR:
  5976. Result := 'STAR';
  5977. OP_PLUS:
  5978. Result := 'PLUS';
  5979. OP_BRACES:
  5980. Result := 'BRACES';
  5981. {$IFDEF ComplexBraces}
  5982. OP_LOOPENTRY:
  5983. Result := 'LOOPENTRY';
  5984. OP_LOOP:
  5985. Result := 'LOOP';
  5986. OP_LOOPNG:
  5987. Result := 'LOOPNG';
  5988. {$ENDIF}
  5989. OP_STARNG:
  5990. Result := 'STARNG';
  5991. OP_PLUSNG:
  5992. Result := 'PLUSNG';
  5993. OP_BRACESNG:
  5994. Result := 'BRACESNG';
  5995. OP_STAR_POSS:
  5996. Result := 'STAR_POSS';
  5997. OP_PLUS_POSS:
  5998. Result := 'PLUS_POSS';
  5999. OP_BRACES_POSS:
  6000. Result := 'BRACES_POSS';
  6001. OP_ANYCATEGORY:
  6002. Result := 'ANYCATEG';
  6003. OP_NOTCATEGORY:
  6004. Result := 'NOTCATEG';
  6005. OP_RECUR:
  6006. Result := 'RECURSION';
  6007. OP_SUBCALL_FIRST .. OP_SUBCALL_LAST:
  6008. Result := Format('SUBCALL[%d]', [Ord(op) - Ord(OP_SUBCALL)]);
  6009. else
  6010. Error(reeDumpCorruptedOpcode);
  6011. end;
  6012. end; { of function TRegExpr.DumpOp
  6013. -------------------------------------------------------------- }
  6014. function TRegExpr.IsCompiled: boolean;
  6015. begin
  6016. Result := programm <> nil;
  6017. end;
  6018. function PrintableChar(AChar: REChar): RegExprString; {$IFDEF InlineFuncs}inline;{$ENDIF}
  6019. begin
  6020. if AChar < ' ' then
  6021. Result := '#' + IntToStr(Ord(AChar))
  6022. else
  6023. Result := AChar;
  6024. end;
  6025. function TRegExpr.DumpCheckerIndex(N: byte): RegExprString;
  6026. begin
  6027. Result := '?';
  6028. if N = CheckerIndex_Word then Result := '\w' else
  6029. if N = CheckerIndex_NotWord then Result := '\W' else
  6030. if N = CheckerIndex_Digit then Result := '\d' else
  6031. if N = CheckerIndex_NotDigit then Result := '\D' else
  6032. if N = CheckerIndex_Space then Result := '\s' else
  6033. if N = CheckerIndex_NotSpace then Result := '\S' else
  6034. if N = CheckerIndex_HorzSep then Result := '\h' else
  6035. if N = CheckerIndex_NotHorzSep then Result := '\H' else
  6036. if N = CheckerIndex_VertSep then Result := '\v' else
  6037. if N = CheckerIndex_NotVertSep then Result := '\V' else
  6038. if N = CheckerIndex_LowerAZ then Result := 'az' else
  6039. if N = CheckerIndex_UpperAZ then Result := 'AZ' else
  6040. ;
  6041. end;
  6042. function TRegExpr.DumpCategoryChars(ch, ch2: REChar; Positive: boolean): RegExprString;
  6043. const
  6044. S: array[boolean] of RegExprString = ('P', 'p');
  6045. begin
  6046. Result := '\' + S[Positive] + '{' + ch;
  6047. if ch2 <> #0 then
  6048. Result := Result + ch2;
  6049. Result := Result + '} ';
  6050. end;
  6051. function TRegExpr.Dump: RegExprString;
  6052. // dump a regexp in vaguely comprehensible form
  6053. var
  6054. s: PRegExprChar;
  6055. op: TREOp; // Arbitrary non-END op.
  6056. next: PRegExprChar;
  6057. i, NLen: integer;
  6058. Diff: PtrInt;
  6059. iByte: byte;
  6060. ch, ch2: REChar;
  6061. begin
  6062. if not IsProgrammOk then
  6063. Exit;
  6064. op := OP_EXACTLY;
  6065. Result := '';
  6066. s := regCodeWork;
  6067. while op <> OP_EEND do
  6068. begin // While that wasn't END last time...
  6069. op := s^;
  6070. Result := Result + Format('%2d: %s', [s - programm, DumpOp(s^)]);
  6071. // Where, what.
  6072. next := regNext(s);
  6073. if next = nil // Next ptr.
  6074. then
  6075. Result := Result + ' (0)'
  6076. else
  6077. begin
  6078. if next > s
  6079. // ###0.948 PWideChar subtraction workaround (see comments in Tail method for details)
  6080. then
  6081. Diff := next - s
  6082. else
  6083. Diff := -(s - next);
  6084. Result := Result + Format(' (%d) ', [(s - programm) + Diff]);
  6085. end;
  6086. Inc(s, REOpSz + RENextOffSz);
  6087. if (op = OP_ANYOF) or (op = OP_ANYOFCI) or (op = OP_ANYBUT) or (op = OP_ANYBUTCI) then
  6088. begin
  6089. repeat
  6090. case s^ of
  6091. OpKind_End:
  6092. begin
  6093. Inc(s);
  6094. Break;
  6095. end;
  6096. OpKind_Range:
  6097. begin
  6098. Result := Result + 'Rng(';
  6099. Inc(s);
  6100. Result := Result + PrintableChar(s^) + '-';
  6101. Inc(s);
  6102. Result := Result + PrintableChar(s^);
  6103. Result := Result + ') ';
  6104. Inc(s);
  6105. end;
  6106. OpKind_MetaClass:
  6107. begin
  6108. Inc(s);
  6109. Result := Result + DumpCheckerIndex(byte(s^)) + ' ';
  6110. Inc(s);
  6111. end;
  6112. OpKind_Char:
  6113. begin
  6114. Inc(s);
  6115. NLen := PLongInt(s)^;
  6116. Inc(s, RENumberSz);
  6117. Result := Result + 'Ch(';
  6118. for i := 1 to NLen do
  6119. begin
  6120. Result := Result + PrintableChar(s^);
  6121. Inc(s);
  6122. end;
  6123. Result := Result + ') ';
  6124. end;
  6125. OpKind_CategoryYes:
  6126. begin
  6127. Inc(s);
  6128. ch := s^;
  6129. Inc(s);
  6130. ch2 := s^;
  6131. Result := Result + DumpCategoryChars(ch, ch2, True);
  6132. Inc(s);
  6133. end;
  6134. OpKind_CategoryNo:
  6135. begin
  6136. Inc(s);
  6137. ch := s^;
  6138. Inc(s);
  6139. ch2 := s^;
  6140. Result := Result + DumpCategoryChars(ch, ch2, False);
  6141. Inc(s);
  6142. end;
  6143. else
  6144. Error(reeDumpCorruptedOpcode);
  6145. end;
  6146. until false;
  6147. end;
  6148. if (op = OP_EXACTLY) or (op = OP_EXACTLYCI) then
  6149. begin
  6150. // Literal string, where present.
  6151. NLen := PLongInt(s)^;
  6152. Inc(s, RENumberSz);
  6153. for i := 1 to NLen do
  6154. begin
  6155. Result := Result + PrintableChar(s^);
  6156. Inc(s);
  6157. end;
  6158. end;
  6159. if (op = OP_BSUBEXP) or (op = OP_BSUBEXPCI) then
  6160. begin
  6161. Result := Result + ' \' + IntToStr(Ord(s^));
  6162. Inc(s);
  6163. end;
  6164. if (op = OP_BRACES) or (op = OP_BRACESNG) or (op = OP_BRACES_POSS) then
  6165. begin // ###0.941
  6166. // show min/max argument of braces operator
  6167. Result := Result + Format('{%d,%d}', [PREBracesArg(AlignToInt(s))^,
  6168. PREBracesArg(AlignToInt(s + REBracesArgSz))^]);
  6169. Inc(s, REBracesArgSz * 2);
  6170. end;
  6171. {$IFDEF ComplexBraces}
  6172. if (op = OP_LOOP) or (op = OP_LOOPNG) then
  6173. begin // ###0.940
  6174. Result := Result + Format(' -> (%d) {%d,%d}',
  6175. [(s - programm - (REOpSz + RENextOffSz)) +
  6176. PRENextOff(AlignToPtr(s + 2 * REBracesArgSz))^,
  6177. PREBracesArg(AlignToInt(s))^,
  6178. PREBracesArg(AlignToInt(s + REBracesArgSz))^]);
  6179. Inc(s, 2 * REBracesArgSz + RENextOffSz);
  6180. end;
  6181. {$ENDIF}
  6182. if (op = OP_ANYCATEGORY) or (op = OP_NOTCATEGORY) then
  6183. begin
  6184. ch := s^;
  6185. Inc(s);
  6186. ch2 := s^;
  6187. Inc(s);
  6188. if ch2<>#0 then
  6189. Result := Result + '{' + ch + ch2 + '}'
  6190. else
  6191. Result := Result + '{' + ch + '}';
  6192. end;
  6193. Result := Result + #$d#$a;
  6194. end; { of while }
  6195. // Header fields of interest.
  6196. if regAnchored <> #0 then
  6197. Result := Result + 'Anchored; ';
  6198. if regMustString <> '' then
  6199. Result := Result + 'Must have: "' + regMustString + '"; ';
  6200. {$IFDEF UseFirstCharSet} // ###0.929
  6201. Result := Result + #$d#$a'First charset: ';
  6202. if FirstCharSet = [] then
  6203. Result := Result + '<empty set>'
  6204. else
  6205. if FirstCharSet = RegExprAllSet then
  6206. Result := Result + '<all chars>'
  6207. else
  6208. for iByte := 0 to 255 do
  6209. if iByte in FirstCharSet then
  6210. Result := Result + PrintableChar(REChar(iByte));
  6211. {$ENDIF}
  6212. Result := Result + #$d#$a;
  6213. end; { of function TRegExpr.Dump
  6214. -------------------------------------------------------------- }
  6215. {$ENDIF}
  6216. function TRegExpr.IsFixedLength(var op: TREOp; var ALen: integer): boolean;
  6217. var
  6218. s, next: PRegExprChar;
  6219. N, N2: integer;
  6220. begin
  6221. Result := False;
  6222. ALen := 0;
  6223. if not IsCompiled then Exit;
  6224. s := regCodeWork;
  6225. repeat
  6226. next := regNext(s);
  6227. op := s^;
  6228. Inc(s, REOpSz + RENextOffSz);
  6229. case op of
  6230. OP_EEND:
  6231. begin
  6232. Result := True;
  6233. Exit;
  6234. end;
  6235. OP_BRANCH:
  6236. begin
  6237. op := next^;
  6238. if op <> OP_EEND then Exit;
  6239. end;
  6240. OP_COMMENT,
  6241. OP_BOUND,
  6242. OP_NOTBOUND:
  6243. Continue;
  6244. OP_ANY,
  6245. OP_ANYML,
  6246. OP_ANYDIGIT,
  6247. OP_NOTDIGIT,
  6248. OP_ANYLETTER,
  6249. OP_NOTLETTER,
  6250. OP_ANYSPACE,
  6251. OP_NOTSPACE,
  6252. OP_ANYHORZSEP,
  6253. OP_NOTHORZSEP,
  6254. OP_ANYVERTSEP,
  6255. OP_NOTVERTSEP:
  6256. begin
  6257. Inc(ALen);
  6258. Continue;
  6259. end;
  6260. OP_ANYOF,
  6261. OP_ANYOFCI,
  6262. OP_ANYBUT,
  6263. OP_ANYBUTCI:
  6264. begin
  6265. Inc(ALen);
  6266. repeat
  6267. case s^ of
  6268. OpKind_End:
  6269. begin
  6270. Inc(s);
  6271. Break;
  6272. end;
  6273. OpKind_Range:
  6274. begin
  6275. Inc(s);
  6276. Inc(s);
  6277. Inc(s);
  6278. end;
  6279. OpKind_MetaClass:
  6280. begin
  6281. Inc(s);
  6282. Inc(s);
  6283. end;
  6284. OpKind_Char:
  6285. begin
  6286. Inc(s);
  6287. Inc(s, RENumberSz + PLongInt(s)^);
  6288. end;
  6289. OpKind_CategoryYes,
  6290. OpKind_CategoryNo:
  6291. begin
  6292. Inc(s);
  6293. Inc(s);
  6294. Inc(s);
  6295. end;
  6296. end;
  6297. until False;
  6298. end;
  6299. OP_EXACTLY,
  6300. OP_EXACTLYCI:
  6301. begin
  6302. N := PLongInt(s)^;
  6303. Inc(ALen, N);
  6304. Inc(s, RENumberSz + N);
  6305. Continue;
  6306. end;
  6307. OP_ANYCATEGORY,
  6308. OP_NOTCATEGORY:
  6309. begin
  6310. Inc(ALen);
  6311. Inc(s, 2);
  6312. Continue;
  6313. end;
  6314. OP_BRACES,
  6315. OP_BRACESNG,
  6316. OP_BRACES_POSS:
  6317. begin
  6318. // allow only d{n,n}
  6319. N := PREBracesArg(AlignToInt(s))^;
  6320. N2 := PREBracesArg(AlignToInt(s + REBracesArgSz))^;
  6321. if N <> N2 then
  6322. Exit;
  6323. Inc(ALen, N-1);
  6324. Inc(s, REBracesArgSz * 2);
  6325. end;
  6326. else
  6327. Exit;
  6328. end;
  6329. until False;
  6330. end;
  6331. {$IFDEF reRealExceptionAddr}
  6332. {$OPTIMIZATION ON}
  6333. // ReturnAddr works correctly only if compiler optimization is ON
  6334. // I placed this method at very end of unit because there are no
  6335. // way to restore compiler optimization flag ...
  6336. {$ENDIF}
  6337. procedure TRegExpr.Error(AErrorID: integer);
  6338. {$IFNDEF LINUX}
  6339. {$IFDEF reRealExceptionAddr}
  6340. function ReturnAddr: Pointer; // ###0.938
  6341. asm
  6342. mov eax,[ebp+4]
  6343. end;
  6344. {$ENDIF}
  6345. {$ENDIF}
  6346. var
  6347. e: ERegExpr;
  6348. Msg: string;
  6349. begin
  6350. fLastError := AErrorID; // dummy stub - useless because will raise exception
  6351. Msg := ErrorMsg(AErrorID);
  6352. // compilation error ?
  6353. if AErrorID < reeFirstRuntimeCode then
  6354. Msg := Msg + ' (pos ' + IntToStr(CompilerErrorPos) + ')';
  6355. e := ERegExpr.Create(Msg);
  6356. e.ErrorCode := AErrorID;
  6357. e.CompilerErrorPos := CompilerErrorPos;
  6358. raise e
  6359. {$IFNDEF LINUX}
  6360. {$IFDEF reRealExceptionAddr}
  6361. at ReturnAddr
  6362. {$ENDIF}
  6363. {$ENDIF};
  6364. end; { of procedure TRegExpr.Error
  6365. -------------------------------------------------------------- }
  6366. {$IFDEF Compat} // APIs needed only for users of old FPC 3.0
  6367. function TRegExpr.ExecPos(AOffset: integer; ATryOnce: boolean): boolean; overload;
  6368. begin
  6369. Result := ExecPrim(AOffset, ATryOnce, False, False);
  6370. end;
  6371. function TRegExpr.OldInvertCase(const Ch: REChar): REChar;
  6372. begin
  6373. Result := _UpperCase(Ch);
  6374. if Result = Ch then
  6375. Result := _LowerCase(Ch);
  6376. end;
  6377. class function TRegExpr.InvertCaseFunction(const Ch: REChar): REChar;
  6378. begin
  6379. Result := _UpperCase(Ch);
  6380. if Result = Ch then
  6381. Result := _LowerCase(Ch);
  6382. end;
  6383. function TRegExpr.GetLinePairedSeparator: RegExprString;
  6384. begin
  6385. // not supported anymore
  6386. Result := '';
  6387. end;
  6388. procedure TRegExpr.SetLinePairedSeparator(const AValue: RegExprString);
  6389. begin
  6390. // not supported anymore
  6391. end;
  6392. procedure TRegExpr.SetUseOsLineEndOnReplace(AValue: boolean);
  6393. begin
  6394. if fUseOsLineEndOnReplace = AValue then
  6395. Exit;
  6396. fUseOsLineEndOnReplace := AValue;
  6397. if fUseOsLineEndOnReplace then
  6398. fReplaceLineEnd := sLineBreak
  6399. else
  6400. fReplaceLineEnd := #10;
  6401. end;
  6402. {$ENDIF}
  6403. end.