regexpr.pas 159 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373
  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. interface
  37. { off $DEFINE DebugSynRegExpr }
  38. {$MODE DELPHI} // Delphi-compatible mode in FreePascal
  39. // Disabling for now, seems to cause bug in Lazarus (bug ID 36603)
  40. {$INLINE ON}
  41. // ======== Define base compiler options
  42. {$BOOLEVAL OFF}
  43. {$EXTENDEDSYNTAX ON}
  44. {$LONGSTRINGS ON}
  45. { use optimization settings passed via fpmake/make }
  46. {OPTIMIZATION ON}
  47. // ======== Define options for TRegExpr engine
  48. {$DEFINE UseFirstCharSet} // Enable optimization, which finds possible first chars of input string
  49. {$DEFINE RegExpPCodeDump} // Enable method Dump() to show opcode as string
  50. {$DEFINE ComplexBraces} // Support braces in complex cases
  51. {$IFNDEF UniCode}
  52. {$UNDEF UnicodeWordDetection}
  53. {$ELSE}
  54. {$DEFINE UnicodeWordDetection}
  55. {$ENDIF}
  56. uses
  57. Math, // Min
  58. Classes, // TStrings in Split method
  59. SysUtils; // Exception
  60. type
  61. {$IFDEF UniCode}
  62. PRegExprChar = PWideChar;
  63. RegExprString = UnicodeString;
  64. REChar = WideChar;
  65. {$ELSE}
  66. PRegExprChar = PChar;
  67. RegExprString = AnsiString; // ###0.952 was string
  68. REChar = Char;
  69. {$ENDIF}
  70. TREOp = REChar; // internal p-code type //###0.933
  71. PREOp = ^TREOp;
  72. type
  73. TRegExprInvertCaseFunction = function(const Ch: REChar): REChar of object;
  74. TRegExprCharset = set of byte;
  75. const
  76. // Escape char ('\' in common r.e.) used for escaping metachars (\w, \d etc)
  77. EscChar = '\';
  78. RegExprModifierI: boolean = False; // default value for ModifierI
  79. RegExprModifierR: boolean = True; // default value for ModifierR
  80. RegExprModifierS: boolean = True; // default value for ModifierS
  81. RegExprModifierG: boolean = True; // default value for ModifierG
  82. RegExprModifierM: boolean = False; // default value for ModifierM
  83. RegExprModifierX: boolean = False; // default value for ModifierX
  84. // default value for SpaceChars
  85. RegExprSpaceChars: RegExprString = ' '#$9#$A#$D#$C;
  86. // default value for WordChars
  87. RegExprWordChars: RegExprString = '0123456789'
  88. + 'abcdefghijklmnopqrstuvwxyz'
  89. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';
  90. // default value for LineSeparators
  91. RegExprLineSeparators: RegExprString = #$d#$a#$b#$c
  92. {$IFDEF UniCode}
  93. + #$2028#$2029#$85
  94. {$ENDIF};
  95. // default value for LinePairedSeparator
  96. RegExprLinePairedSeparator: RegExprString = #$d#$a;
  97. { if You need Unix-styled line separators (only \n), then use:
  98. RegExprLineSeparators = #$a;
  99. RegExprLinePairedSeparator = '';
  100. }
  101. // Tab and Unicode category "Space Separator":
  102. // https://www.compart.com/en/unicode/category/Zs
  103. RegExprHorzSeparators: RegExprString = #9#$20#$A0
  104. {$IFDEF UniCode}
  105. + #$1680#$2000#$2001#$2002#$2003#$2004#$2005#$2006#$2007#$2008#$2009#$200A#$202F#$205F#$3000
  106. {$ENDIF};
  107. const
  108. NSUBEXP = 90; // max number of subexpression //###0.929
  109. // Cannot be more than NSUBEXPMAX
  110. // Be carefull - don't use values which overflow CLOSE opcode
  111. // (in this case you'll get compiler error).
  112. // Big NSUBEXP will cause more slow work and more stack required
  113. NSUBEXPMAX = 255; // Max possible value for NSUBEXP. //###0.945
  114. // Don't change it! It's defined by internal TRegExpr design.
  115. {$IFDEF ComplexBraces}
  116. const
  117. LoopStackMax = 10; // max depth of loops stack //###0.925
  118. type
  119. TRegExprLoopStack = array [1 .. LoopStackMax] of integer;
  120. {$ENDIF}
  121. type
  122. TRegExprModifiers = record
  123. I: boolean;
  124. // Case-insensitive.
  125. R: boolean;
  126. // Extended syntax for Russian ranges in [].
  127. // If True, then а-я additionally includes letter 'ё',
  128. // А-Я additionally includes 'Ё', and а-Я includes all Russian letters.
  129. // Turn it off if it interferes with your national alphabet.
  130. S: boolean;
  131. // Dot '.' matches any char, otherwise only [^\n].
  132. G: boolean;
  133. // Greedy. Switching it off switches all operators to non-greedy style,
  134. // so if G=False, then '*' works like '*?', '+' works like '+?' and so on.
  135. M: boolean;
  136. // Treat string as multiple lines. It changes `^' and `$' from
  137. // matching at only the very start/end of the string to the start/end
  138. // of any line anywhere within the string.
  139. X: boolean;
  140. // Allow comments in regex using # char.
  141. end;
  142. function IsModifiersEqual(const A, B: TRegExprModifiers): boolean;
  143. type
  144. TRegExpr = class;
  145. TRegExprReplaceFunction = function(ARegExpr: TRegExpr): RegExprString of object;
  146. TRegExprCharChecker = function(ch: REChar): boolean of object;
  147. TRegExprCharCheckerArray = array[0 .. 30] of TRegExprCharChecker;
  148. TRegExprCharCheckerInfo = record
  149. CharBegin, CharEnd: REChar;
  150. CheckerIndex: integer;
  151. end;
  152. TRegExprCharCheckerInfos = array of TRegExprCharCheckerInfo;
  153. { TRegExpr }
  154. TRegExpr = class
  155. private
  156. startp: array [0 .. NSUBEXP - 1] of PRegExprChar; // found expr start points
  157. endp: array [0 .. NSUBEXP - 1] of PRegExprChar; // found expr end points
  158. GrpIndexes: array [0 .. NSUBEXP - 1] of integer;
  159. GrpCount: integer;
  160. {$IFDEF ComplexBraces}
  161. LoopStack: TRegExprLoopStack; // state before entering loop
  162. LoopStackIdx: integer; // 0 - out of all loops
  163. {$ENDIF}
  164. // The "internal use only" fields to pass info from compile
  165. // to execute that permits the execute phase to run lots faster on
  166. // simple cases.
  167. reganchored: REChar; // is the match anchored (at beginning-of-line only)?
  168. regmust: PRegExprChar; // string (pointer into program) that match must include, or nil
  169. regmustlen: integer; // length of regmust string
  170. regmustString: RegExprString;
  171. // reganchored permits very fast decisions on suitable starting points
  172. // for a match, cutting down the work a lot. Regmust permits fast rejection
  173. // of lines that cannot possibly match. The regmust tests are costly enough
  174. // that regcomp() supplies a regmust only if the r.e. contains something
  175. // potentially expensive (at present, the only such thing detected is * or +
  176. // at the start of the r.e., which can involve a lot of backup). regmustlen is
  177. // supplied because the test in regexec() needs it and regcomp() is computing
  178. // it anyway.
  179. {$IFDEF UseFirstCharSet}
  180. FirstCharSet: TRegExprCharset;
  181. FirstCharArray: array[byte] of boolean;
  182. {$ENDIF}
  183. // work variables for Exec routines - save stack in recursion
  184. reginput: PRegExprChar; // String-input pointer.
  185. fInputStart: PRegExprChar; // Pointer to first char of input string.
  186. fInputEnd: PRegExprChar; // Pointer to char AFTER last char of input string
  187. fRegexStart: PRegExprChar;
  188. fRegexEnd: PRegExprChar;
  189. // work variables for compiler's routines
  190. regparse: PRegExprChar; // Input-scan pointer.
  191. regnpar: integer; // Count of () brackets.
  192. regdummy: REChar;
  193. regcode: PRegExprChar; // Code-emit pointer; @regdummy = don't.
  194. regsize: integer; // Total programm size in REChars.
  195. regExactlyLen: PLongInt;
  196. regexpBegin: PRegExprChar; // only for error handling. Contains pointer to beginning of r.e. while compiling
  197. regexpIsCompiled: boolean; // true if r.e. successfully compiled
  198. fSecondPass: boolean;
  199. // programm is essentially a linear encoding
  200. // of a nondeterministic finite-state machine (aka syntax charts or
  201. // "railroad normal form" in parsing technology). Each node is an opcode
  202. // plus a "next" pointer, possibly plus an operand. "Next" pointers of
  203. // all nodes except BRANCH implement concatenation; a "next" pointer with
  204. // a BRANCH on both ends of it connects two alternatives. (Here we
  205. // have one of the subtle syntax dependencies: an individual BRANCH (as
  206. // opposed to a collection of them) is never concatenated with anything
  207. // because of operator precedence.) The operand of some types of node is
  208. // a literal string; for others, it is a node leading into a sub-FSM. In
  209. // particular, the operand of a BRANCH node is the first node of the branch.
  210. // (NB this is *not* a tree structure: the tail of the branch connects
  211. // to the thing following the set of BRANCHes.) The opcodes are:
  212. programm: PRegExprChar; // Unwarranted chumminess with compiler.
  213. fExpression: RegExprString; // source of compiled r.e.
  214. fInputString: RegExprString; // input string
  215. fLastError: integer; // see Error, LastError
  216. fLastErrorOpcode: TREOp;
  217. fModifiers: TRegExprModifiers; // modifiers
  218. fCompModifiers: TRegExprModifiers; // compiler's copy of modifiers
  219. fProgModifiers: TRegExprModifiers; // modifiers values from last programm compilation
  220. fSpaceChars: RegExprString;
  221. fWordChars: RegExprString;
  222. fInvertCase: TRegExprInvertCaseFunction;
  223. fLineSeparators: RegExprString;
  224. fLinePairedSeparatorAssigned: boolean;
  225. fLinePairedSeparatorHead, fLinePairedSeparatorTail: REChar;
  226. FReplaceLineEnd: RegExprString; // string to use for "\n" in Substitute method
  227. FUseOsLineEndOnReplace: boolean; // use OS LineBreak chars (LF or CRLF) for FReplaceLineEnd
  228. fSlowChecksSizeMax: integer;
  229. // use ASlowChecks=True in Exec() only when Length(InputString)<SlowChecksSizeMax
  230. // ASlowChecks enables to use regmustString optimization
  231. {$IFNDEF UniCode}
  232. fLineSepArray: array[byte] of boolean;
  233. {$ENDIF}
  234. {$IFDEF UnicodeWordDetection}
  235. FUseUnicodeWordDetection: boolean;
  236. {$ENDIF}
  237. FEmptyInputRaisesError : Boolean;
  238. CharCheckers: TRegExprCharCheckerArray;
  239. CharCheckerInfos: TRegExprCharCheckerInfos;
  240. CheckerIndex_Word: byte;
  241. CheckerIndex_NotWord: byte;
  242. CheckerIndex_Digit: byte;
  243. CheckerIndex_NotDigit: byte;
  244. CheckerIndex_Space: byte;
  245. CheckerIndex_NotSpace: byte;
  246. CheckerIndex_HorzSep: byte;
  247. CheckerIndex_NotHorzSep: byte;
  248. CheckerIndex_VertSep: byte;
  249. CheckerIndex_NotVertSep: byte;
  250. CheckerIndex_LowerAZ: byte;
  251. CheckerIndex_UpperAZ: byte;
  252. procedure InitCharCheckers;
  253. function CharChecker_Word(ch: REChar): boolean;
  254. function CharChecker_NotWord(ch: REChar): boolean;
  255. function CharChecker_Space(ch: REChar): boolean;
  256. function CharChecker_NotSpace(ch: REChar): boolean;
  257. function CharChecker_Digit(ch: REChar): boolean;
  258. function CharChecker_NotDigit(ch: REChar): boolean;
  259. function CharChecker_HorzSep(ch: REChar): boolean;
  260. function CharChecker_NotHorzSep(ch: REChar): boolean;
  261. function CharChecker_VertSep(ch: REChar): boolean;
  262. function CharChecker_NotVertSep(ch: REChar): boolean;
  263. function CharChecker_LowerAZ(ch: REChar): boolean;
  264. function CharChecker_UpperAZ(ch: REChar): boolean;
  265. procedure ClearMatches; {$IFDEF InlineFuncs}inline;{$ENDIF}
  266. procedure ClearInternalIndexes; {$IFDEF InlineFuncs}inline;{$ENDIF}
  267. function FindInCharClass(ABuffer: PRegExprChar; AChar: REChar; AIgnoreCase: boolean): boolean;
  268. procedure GetCharSetFromCharClass(ABuffer: PRegExprChar; AIgnoreCase: boolean; var ARes: TRegExprCharset);
  269. procedure GetCharSetFromSpaceChars(var ARes: TRegExprCharset);
  270. procedure GetCharSetFromWordChars(var ARes: TRegExprCharSet);
  271. function IsWordChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  272. function IsSpaceChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  273. function IsCustomLineSeparator(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  274. procedure InitLineSepArray;
  275. // Mark programm as having to be [re]compiled
  276. procedure InvalidateProgramm;
  277. // Check if we can use precompiled r.e. or
  278. // [re]compile it if something changed
  279. function IsProgrammOk: boolean; // ###0.941
  280. procedure SetExpression(const AStr: RegExprString);
  281. function GetModifierStr: RegExprString;
  282. procedure SetModifierStr(const AStr: RegExprString);
  283. function GetModifierG: boolean;
  284. function GetModifierI: boolean;
  285. function GetModifierM: boolean;
  286. function GetModifierR: boolean;
  287. function GetModifierS: boolean;
  288. function GetModifierX: boolean;
  289. procedure SetModifierG(AValue: boolean);
  290. procedure SetModifierI(AValue: boolean);
  291. procedure SetModifierM(AValue: boolean);
  292. procedure SetModifierR(AValue: boolean);
  293. procedure SetModifierS(AValue: boolean);
  294. procedure SetModifierX(AValue: boolean);
  295. // Default handler raises exception ERegExpr with
  296. // Message = ErrorMsg (AErrorID), ErrorCode = AErrorID
  297. // and CompilerErrorPos = value of property CompilerErrorPos.
  298. procedure Error(AErrorID: integer); virtual; // error handler.
  299. { ==================== Compiler section =================== }
  300. // compile a regular expression into internal code
  301. function CompileRegExpr(ARegExp: PRegExprChar): boolean;
  302. procedure SetUseOsLineEndOnReplace(AValue: boolean);
  303. // set the next-pointer at the end of a node chain
  304. procedure Tail(p: PRegExprChar; val: PRegExprChar);
  305. // regoptail - regtail on operand of first argument; nop if operandless
  306. procedure OpTail(p: PRegExprChar; val: PRegExprChar);
  307. // regnode - emit a node, return location
  308. function EmitNode(op: TREOp): PRegExprChar;
  309. // emit (if appropriate) a byte of code
  310. procedure EmitC(ch: REChar);
  311. // emit LongInt value
  312. procedure EmitInt(AValue: LongInt);
  313. // insert an operator in front of already-emitted operand
  314. // Means relocating the operand.
  315. procedure InsertOperator(op: TREOp; opnd: PRegExprChar; sz: integer);
  316. // ###0.90
  317. // regular expression, i.e. main body or parenthesized thing
  318. function ParseReg(paren: integer; var flagp: integer): PRegExprChar;
  319. // one alternative of an | operator
  320. function ParseBranch(var flagp: integer): PRegExprChar;
  321. // something followed by possible [*+?]
  322. function ParsePiece(var flagp: integer): PRegExprChar;
  323. function HexDig(Ch: REChar): integer;
  324. function UnQuoteChar(var APtr: PRegExprChar): REChar;
  325. // the lowest level
  326. function ParseAtom(var flagp: integer): PRegExprChar;
  327. // current pos in r.e. - for error hanling
  328. function GetCompilerErrorPos: PtrInt;
  329. {$IFDEF UseFirstCharSet} // ###0.929
  330. procedure FillFirstCharSet(prog: PRegExprChar);
  331. {$ENDIF}
  332. { ===================== Matching section =================== }
  333. // repeatedly match something simple, report how many
  334. function regrepeat(p: PRegExprChar; AMax: integer): integer;
  335. // dig the "next" pointer out of a node
  336. function regnext(p: PRegExprChar): PRegExprChar;
  337. // recursively matching routine
  338. function MatchPrim(prog: PRegExprChar): boolean;
  339. // match at specific position only, called from ExecPrim
  340. function MatchAtOnePos(APos: PRegExprChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  341. // Exec for stored InputString
  342. function ExecPrim(AOffset: integer; ATryOnce, ASlowChecks, ABackward: boolean): boolean;
  343. {$IFDEF RegExpPCodeDump}
  344. function DumpOp(op: TREOp): RegExprString;
  345. {$ENDIF}
  346. function GetSubExprCount: integer;
  347. function GetMatchPos(Idx: integer): PtrInt;
  348. function GetMatchLen(Idx: integer): PtrInt;
  349. function GetMatch(Idx: integer): RegExprString;
  350. procedure SetInputString(const AInputString: RegExprString);
  351. procedure SetLineSeparators(const AStr: RegExprString);
  352. procedure SetLinePairedSeparator(const AStr: RegExprString);
  353. function GetLinePairedSeparator: RegExprString;
  354. public
  355. constructor Create; overload;
  356. constructor Create(const AExpression: RegExprString); overload;
  357. destructor Destroy; override;
  358. class function VersionMajor: integer;
  359. class function VersionMinor: integer;
  360. // match a programm against a string AInputString
  361. // !!! Exec store AInputString into InputString property
  362. // For Delphi 5 and higher available overloaded versions - first without
  363. // parameter (uses already assigned to InputString property value)
  364. // and second that has int parameter and is same as ExecPos
  365. function Exec(const AInputString: RegExprString): boolean; overload;
  366. function Exec: boolean; overload;
  367. function Exec(AOffset: integer): boolean; overload;
  368. // find next match:
  369. // ExecNext;
  370. // works the same as
  371. // if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)
  372. // else ExecPos (MatchPos [0] + MatchLen [0]);
  373. // but it's more simpler !
  374. // Raises exception if used without preceeding SUCCESSFUL call to
  375. // Exec* (Exec, ExecPos, ExecNext). So You always must use something like
  376. // if Exec (InputString) then repeat { proceed results} until not ExecNext;
  377. function ExecNext: boolean; overload;
  378. function ExecNext(ABackward: boolean): boolean; overload;
  379. // find match for InputString starting from AOffset position
  380. // (AOffset=1 - first char of InputString)
  381. function ExecPos(AOffset: integer = 1): boolean; overload;
  382. function ExecPos(AOffset: integer; ATryOnce: boolean): boolean; overload;
  383. function ExecPos(AOffset: integer; ATryOnce, ABackward: boolean): boolean; overload;
  384. // Returns ATemplate with '$&' or '$0' replaced by whole r.e.
  385. // occurence and '$1'...'$nn' replaced by subexpression with given index.
  386. // Symbol '$' is used instead of '\' (for future extensions
  387. // and for more Perl-compatibility) and accepts more than one digit.
  388. // If you want to place into template raw '$' or '\', use prefix '\'.
  389. // Example: '1\$ is $2\\rub\\' -> '1$ is <Match[2]>\rub\'
  390. // If you want to place any number after '$' you must enclose it
  391. // with curly braces: '${12}'.
  392. // Example: 'a$12bc' -> 'a<Match[12]>bc'
  393. // 'a${1}2bc' -> 'a<Match[1]>2bc'.
  394. function Substitute(const ATemplate: RegExprString): RegExprString;
  395. // Splits AInputStr to list by positions of all r.e. occurencies.
  396. // Internally calls Exec, ExecNext.
  397. procedure Split(const AInputStr: RegExprString; APieces: TStrings);
  398. function Replace(const AInputStr: RegExprString;
  399. const AReplaceStr: RegExprString;
  400. AUseSubstitution: boolean = False) // ###0.946
  401. : RegExprString; overload;
  402. function Replace(const AInputStr: RegExprString;
  403. AReplaceFunc: TRegExprReplaceFunction): RegExprString; overload;
  404. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr.
  405. // If AUseSubstitution is true, then AReplaceStr will be used
  406. // as template for Substitution methods.
  407. // For example:
  408. // Expression := '({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*';
  409. // Replace ('BLOCK( test1)', 'def "$1" value "$2"', True);
  410. // will return: def 'BLOCK' value 'test1'
  411. // Replace ('BLOCK( test1)', 'def "$1" value "$2"')
  412. // will return: def "$1" value "$2"
  413. // Internally calls Exec, ExecNext.
  414. // Overloaded version and ReplaceEx operate with callback function,
  415. // so you can implement really complex functionality.
  416. function ReplaceEx(const AInputStr: RegExprString;
  417. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  418. // Returns ID of last error, 0 if no errors (unusable if
  419. // Error method raises exception) and clear internal status
  420. // into 0 (no errors).
  421. function LastError: integer;
  422. // Returns Error message for error with ID = AErrorID.
  423. function ErrorMsg(AErrorID: integer): RegExprString; virtual;
  424. // Converts Ch into upper case if it in lower case or in lower
  425. // if it in upper (uses current system local setings)
  426. class function InvertCaseFunction(const Ch: REChar): REChar;
  427. // [Re]compile r.e. Useful for example for GUI r.e. editors (to check
  428. // all properties validity).
  429. procedure Compile; // ###0.941
  430. {$IFDEF RegExpPCodeDump}
  431. // dump a compiled regexp in vaguely comprehensible form
  432. function Dump: RegExprString;
  433. {$ENDIF}
  434. // Regular expression.
  435. // For optimization, TRegExpr will automatically compiles it into 'P-code'
  436. // (You can see it with help of Dump method) and stores in internal
  437. // structures. Real [re]compilation occures only when it really needed -
  438. // while calling Exec, ExecNext, Substitute, Dump, etc
  439. // and only if Expression or other P-code affected properties was changed
  440. // after last [re]compilation.
  441. // If any errors while [re]compilation occures, Error method is called
  442. // (by default Error raises exception - see below)
  443. property Expression: RegExprString read fExpression write SetExpression;
  444. // Set/get default values of r.e.syntax modifiers. Modifiers in
  445. // r.e. (?ismx-ismx) will replace this default values.
  446. // If you try to set unsupported modifier, Error will be called
  447. // (by defaul Error raises exception ERegExpr).
  448. property ModifierStr: RegExprString read GetModifierStr write SetModifierStr;
  449. property ModifierI: boolean read GetModifierI write SetModifierI;
  450. property ModifierR: boolean read GetModifierR write SetModifierR;
  451. property ModifierS: boolean read GetModifierS write SetModifierS;
  452. property ModifierG: boolean read GetModifierG write SetModifierG;
  453. property ModifierM: boolean read GetModifierM write SetModifierM;
  454. property ModifierX: boolean read GetModifierX write SetModifierX;
  455. // returns current input string (from last Exec call or last assign
  456. // to this property).
  457. // Any assignment to this property clear Match* properties !
  458. property InputString: RegExprString read fInputString write SetInputString;
  459. // Number of subexpressions has been found in last Exec* call.
  460. // If there are no subexpr. but whole expr was found (Exec* returned True),
  461. // then SubExprMatchCount=0, if no subexpressions nor whole
  462. // r.e. found (Exec* returned false) then SubExprMatchCount=-1.
  463. // Note, that some subexpr. may be not found and for such
  464. // subexpr. MathPos=MatchLen=-1 and Match=''.
  465. // For example: Expression := '(1)?2(3)?';
  466. // Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'
  467. // Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'
  468. // Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'
  469. // Exec ('2'): SubExprMatchCount=0, Match[0]='2'
  470. // Exec ('7') - return False: SubExprMatchCount=-1
  471. property SubExprMatchCount: integer read GetSubExprCount;
  472. // pos of entrance subexpr. #Idx into tested in last Exec*
  473. // string. First subexpr. has Idx=1, last - MatchCount,
  474. // whole r.e. has Idx=0.
  475. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  476. // not found in input string.
  477. property MatchPos[Idx: integer]: PtrInt read GetMatchPos;
  478. // len of entrance subexpr. #Idx r.e. into tested in last Exec*
  479. // string. First subexpr. has Idx=1, last - MatchCount,
  480. // whole r.e. has Idx=0.
  481. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  482. // not found in input string.
  483. // Remember - MatchLen may be 0 (if r.e. match empty string) !
  484. property MatchLen[Idx: integer]: PtrInt read GetMatchLen;
  485. // == copy (InputString, MatchPos [Idx], MatchLen [Idx])
  486. // Returns '' if in r.e. no such subexpr. or this subexpr.
  487. // not found in input string.
  488. property Match[Idx: integer]: RegExprString read GetMatch;
  489. // Returns position in r.e. where compiler stopped.
  490. // Useful for error diagnostics
  491. property CompilerErrorPos: PtrInt read GetCompilerErrorPos;
  492. // Contains chars, treated as /s (initially filled with RegExprSpaceChars
  493. // global constant)
  494. property SpaceChars: RegExprString read fSpaceChars write fSpaceChars;
  495. // ###0.927
  496. // Contains chars, treated as /w (initially filled with RegExprWordChars
  497. // global constant)
  498. property WordChars: RegExprString read fWordChars write fWordChars;
  499. // ###0.929
  500. {$IFDEF UnicodeWordDetection}
  501. // If set to true, in addition to using WordChars, a heuristic to detect unicode word letters is used for \w
  502. property UseUnicodeWordDetection: boolean read FUseUnicodeWordDetection write FUseUnicodeWordDetection;
  503. {$ENDIF}
  504. // line separators (like \n in Unix)
  505. property LineSeparators: RegExprString read fLineSeparators write SetLineSeparators; // ###0.941
  506. // paired line separator (like \r\n in DOS and Windows).
  507. // must contain exactly two chars or no chars at all
  508. property LinePairedSeparator: RegExprString read GetLinePairedSeparator write SetLinePairedSeparator; // ###0.941
  509. // Set this property if you want to override case-insensitive functionality.
  510. // Create set it to RegExprInvertCaseFunction (InvertCaseFunction by default)
  511. property InvertCase: TRegExprInvertCaseFunction read fInvertCase write fInvertCase; // ##0.935
  512. // Use OS line end on replace or not. Default is True for backwards compatibility.
  513. // Set to false to use #10.
  514. property UseOsLineEndOnReplace: boolean read FUseOsLineEndOnReplace write SetUseOsLineEndOnReplace;
  515. property SlowChecksSizeMax: integer read fSlowChecksSizeMax write fSlowChecksSizeMax;
  516. // Raise error when input string is empty
  517. Property EmptyInputRaisesError : Boolean Read FEmptyInputRaisesError Write FEmptyInputRaisesError;
  518. end;
  519. type
  520. ERegExpr = class(Exception)
  521. public
  522. ErrorCode: integer;
  523. CompilerErrorPos: PtrInt;
  524. end;
  525. const
  526. RegExprInvertCaseFunction: TRegExprInvertCaseFunction = nil;
  527. // true if string AInputString match regular expression ARegExpr
  528. // ! will raise exeption if syntax errors in ARegExpr
  529. function ExecRegExpr(const ARegExpr, AInputStr: RegExprString): boolean;
  530. // Split AInputStr into APieces by r.e. ARegExpr occurencies
  531. procedure SplitRegExpr(const ARegExpr, AInputStr: RegExprString;
  532. APieces: TStrings);
  533. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr
  534. // If AUseSubstitution is true, then AReplaceStr will be used
  535. // as template for Substitution methods.
  536. // For example:
  537. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  538. // 'BLOCK( test1)', 'def "$1" value "$2"', True)
  539. // will return: def 'BLOCK' value 'test1'
  540. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  541. // 'BLOCK( test1)', 'def "$1" value "$2"')
  542. // will return: def "$1" value "$2"
  543. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  544. AUseSubstitution: boolean = False): RegExprString; overload; // ###0.947
  545. // Alternate form allowing to set more parameters.
  546. type
  547. TRegexReplaceOption = (
  548. rroModifierI,
  549. rroModifierR,
  550. rroModifierS,
  551. rroModifierG,
  552. rroModifierM,
  553. rroModifierX,
  554. rroUseSubstitution,
  555. rroUseOsLineEnd
  556. );
  557. TRegexReplaceOptions = set of TRegexReplaceOption;
  558. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  559. Options: TRegexReplaceOptions): RegExprString; overload;
  560. // Replace all metachars with its safe representation,
  561. // for example 'abc$cd.(' converts into 'abc\$cd\.\('
  562. // This function useful for r.e. autogeneration from
  563. // user input
  564. function QuoteRegExprMetaChars(const AStr: RegExprString): RegExprString;
  565. // Makes list of subexpressions found in ARegExpr r.e.
  566. // In ASubExps every item represent subexpression,
  567. // from first to last, in format:
  568. // String - subexpression text (without '()')
  569. // low word of Object - starting position in ARegExpr, including '('
  570. // if exists! (first position is 1)
  571. // high word of Object - length, including starting '(' and ending ')'
  572. // if exist!
  573. // AExtendedSyntax - must be True if modifier /m will be On while
  574. // using the r.e.
  575. // Useful for GUI editors of r.e. etc (You can find example of using
  576. // in TestRExp.dpr project)
  577. // Returns
  578. // 0 Success. No unbalanced brackets was found;
  579. // -1 There are not enough closing brackets ')';
  580. // -(n+1) At position n was found opening '[' without //###0.942
  581. // corresponding closing ']';
  582. // n At position n was found closing bracket ')' without
  583. // corresponding opening '('.
  584. // If Result <> 0, then ASubExpr can contain empty items or illegal ones
  585. function RegExprSubExpressions(const ARegExpr: string; ASubExprs: TStrings;
  586. AExtendedSyntax: boolean= False): integer;
  587. implementation
  588. {$IFDEF UnicodeWordDetection}
  589. uses
  590. UnicodeData;
  591. {$ENDIF}
  592. const
  593. // TRegExpr.VersionMajor/Minor return values of these constants:
  594. REVersionMajor = 0;
  595. REVersionMinor = 987;
  596. OpKind_End = REChar(1);
  597. OpKind_MetaClass = REChar(2);
  598. OpKind_Range = REChar(3);
  599. OpKind_Char = REChar(4);
  600. RegExprAllSet = [0 .. 255];
  601. RegExprDigitSet = [Ord('0') .. Ord('9')];
  602. RegExprLowerAzSet = [Ord('a') .. Ord('z')];
  603. RegExprUpperAzSet = [Ord('A') .. Ord('Z')];
  604. RegExprAllAzSet = RegExprLowerAzSet + RegExprUpperAzSet;
  605. RegExprLineSeparatorsSet = [$d, $a, $b, $c] {$IFDEF UniCode} + [$85] {$ENDIF};
  606. RegExprHorzSeparatorsSet = [9, $20, $A0];
  607. MaxBracesArg = $7FFFFFFF - 1; // max value for {n,m} arguments //###0.933
  608. type
  609. TRENextOff = PtrInt;
  610. // internal Next "pointer" (offset to current p-code) //###0.933
  611. PRENextOff = ^TRENextOff;
  612. // used for extracting Next "pointers" from compiled r.e. //###0.933
  613. TREBracesArg = integer; // type of {m,n} arguments
  614. PREBracesArg = ^TREBracesArg;
  615. const
  616. REOpSz = SizeOf(TREOp) div SizeOf(REChar);
  617. // size of OP_ command in REChars
  618. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  619. // add space for aligning pointer
  620. // -1 is the correct max size but also needed for InsertOperator that needs a multiple of pointer size
  621. RENextOffSz = (2 * SizeOf(TRENextOff) div SizeOf(REChar)) - 1;
  622. REBracesArgSz = (2 * SizeOf(TREBracesArg) div SizeOf(REChar));
  623. // add space for aligning pointer
  624. {$ELSE}
  625. RENextOffSz = (SizeOf(TRENextOff) div SizeOf(REChar));
  626. // size of Next pointer in REChars
  627. REBracesArgSz = SizeOf(TREBracesArg) div SizeOf(REChar);
  628. // size of BRACES arguments in REChars
  629. {$ENDIF}
  630. RENumberSz = SizeOf(LongInt) div SizeOf(REChar);
  631. function _FindCharInBuffer(SBegin, SEnd: PRegExprChar; Ch: REChar): PRegExprChar; {$IFDEF InlineFuncs}inline;{$ENDIF}
  632. begin
  633. while SBegin < SEnd do
  634. begin
  635. if SBegin^ = Ch then
  636. begin
  637. Result := SBegin;
  638. Exit;
  639. end;
  640. Inc(SBegin);
  641. end;
  642. Result := nil;
  643. end;
  644. function IsIgnoredChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  645. begin
  646. case AChar of
  647. ' ', #9, #$d, #$a:
  648. Result := True
  649. else
  650. Result := False;
  651. end;
  652. end;
  653. function _IsMetaChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  654. begin
  655. case AChar of
  656. 'd', 'D',
  657. 's', 'S',
  658. 'w', 'W',
  659. 'v', 'V',
  660. 'h', 'H':
  661. Result := True
  662. else
  663. Result := False;
  664. end;
  665. end;
  666. function AlignToPtr(const p: Pointer): Pointer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  667. begin
  668. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  669. Result := Align(p, SizeOf(Pointer));
  670. {$ELSE}
  671. Result := p;
  672. {$ENDIF}
  673. end;
  674. function AlignToInt(const p: Pointer): Pointer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  675. begin
  676. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  677. Result := Align(p, SizeOf(integer));
  678. {$ELSE}
  679. Result := p;
  680. {$ENDIF}
  681. end;
  682. function _UpperCase(Ch: REChar): REChar;
  683. begin
  684. Result := Ch;
  685. if (Ch >= 'a') and (Ch <= 'z') then
  686. begin
  687. Dec(Result, 32);
  688. Exit;
  689. end;
  690. if Ord(Ch) < 128 then
  691. Exit;
  692. {$IFDEF FPC}
  693. {$IFDEF UniCode}
  694. Result := UnicodeUpperCase(Ch)[1];
  695. {$ELSE}
  696. Result := AnsiUpperCase(Ch)[1];
  697. {$ENDIF}
  698. {$ELSE}
  699. {$IFDEF UniCode}
  700. {$IFDEF D2009}
  701. Result := TCharacter.ToUpper(Ch);
  702. {$ENDIF}
  703. {$ELSE}
  704. Result := AnsiUpperCase(Ch)[1];
  705. {$ENDIF}
  706. {$ENDIF}
  707. end;
  708. function _LowerCase(Ch: REChar): REChar;
  709. begin
  710. Result := Ch;
  711. if (Ch >= 'A') and (Ch <= 'Z') then
  712. begin
  713. Inc(Result, 32);
  714. Exit;
  715. end;
  716. if Ord(Ch) < 128 then
  717. Exit;
  718. {$IFDEF FPC}
  719. {$IFDEF UniCode}
  720. Result := UnicodeLowerCase(Ch)[1];
  721. {$ELSE}
  722. Result := AnsiLowerCase(Ch)[1];
  723. {$ENDIF}
  724. {$ELSE}
  725. {$IFDEF UniCode}
  726. {$IFDEF D2009}
  727. Result := TCharacter.ToLower(Ch);
  728. {$ENDIF}
  729. {$ELSE}
  730. Result := AnsiLowerCase(Ch)[1];
  731. {$ENDIF}
  732. {$ENDIF}
  733. end;
  734. { ============================================================= }
  735. { ===================== Global functions ====================== }
  736. { ============================================================= }
  737. function IsModifiersEqual(const A, B: TRegExprModifiers): boolean;
  738. begin
  739. Result :=
  740. (A.I = B.I) and
  741. (A.G = B.G) and
  742. (A.M = B.M) and
  743. (A.S = B.S) and
  744. (A.R = B.R) and
  745. (A.X = B.X);
  746. end;
  747. function ParseModifiers(const APtr: PRegExprChar;
  748. ALen: integer;
  749. var AValue: TRegExprModifiers): boolean;
  750. // Parse string and set AValue if it's in format 'ismxrg-ismxrg'
  751. var
  752. IsOn: boolean;
  753. i: integer;
  754. begin
  755. Result := True;
  756. IsOn := True;
  757. for i := 0 to ALen-1 do
  758. case APtr[i] of
  759. '-':
  760. IsOn := False;
  761. 'I', 'i':
  762. AValue.I := IsOn;
  763. 'R', 'r':
  764. AValue.R := IsOn;
  765. 'S', 's':
  766. AValue.S := IsOn;
  767. 'G', 'g':
  768. AValue.G := IsOn;
  769. 'M', 'm':
  770. AValue.M := IsOn;
  771. 'X', 'x':
  772. AValue.X := IsOn;
  773. else
  774. begin
  775. Result := False;
  776. Exit;
  777. end;
  778. end;
  779. end;
  780. function ExecRegExpr(const ARegExpr, AInputStr: RegExprString): boolean;
  781. var
  782. r: TRegExpr;
  783. begin
  784. r := TRegExpr.Create;
  785. try
  786. r.Expression := ARegExpr;
  787. Result := r.Exec(AInputStr);
  788. finally
  789. r.Free;
  790. end;
  791. end; { of function ExecRegExpr
  792. -------------------------------------------------------------- }
  793. procedure SplitRegExpr(const ARegExpr, AInputStr: RegExprString;
  794. APieces: TStrings);
  795. var
  796. r: TRegExpr;
  797. begin
  798. APieces.Clear;
  799. r := TRegExpr.Create;
  800. try
  801. r.Expression := ARegExpr;
  802. r.Split(AInputStr, APieces);
  803. finally
  804. r.Free;
  805. end;
  806. end; { of procedure SplitRegExpr
  807. -------------------------------------------------------------- }
  808. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  809. AUseSubstitution: boolean= False): RegExprString;
  810. begin
  811. with TRegExpr.Create do
  812. try
  813. Expression := ARegExpr;
  814. Result := Replace(AInputStr, AReplaceStr, AUseSubstitution);
  815. finally
  816. Free;
  817. end;
  818. end; { of function ReplaceRegExpr
  819. -------------------------------------------------------------- }
  820. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  821. Options: TRegexReplaceOptions): RegExprString; overload;
  822. begin
  823. with TRegExpr.Create do
  824. try
  825. ModifierI := (rroModifierI in Options);
  826. ModifierR := (rroModifierR in Options);
  827. ModifierS := (rroModifierS in Options);
  828. ModifierG := (rroModifierG in Options);
  829. ModifierM := (rroModifierM in Options);
  830. ModifierX := (rroModifierX in Options);
  831. // Set this after the above, if the regex contains modifiers, they will be applied.
  832. Expression := ARegExpr;
  833. UseOsLineEndOnReplace := (rroUseOsLineEnd in Options);
  834. Result := Replace(AInputStr, AReplaceStr, rroUseSubstitution in Options);
  835. finally
  836. Free;
  837. end;
  838. end;
  839. (*
  840. const
  841. MetaChars_Init = '^$.[()|?+*' + EscChar + '{';
  842. MetaChars = MetaChars_Init; // not needed to be a variable, const is faster
  843. MetaAll = MetaChars_Init + ']}'; // Very similar to MetaChars, but slighly changed.
  844. *)
  845. function _IsMetaSymbol1(ch: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  846. begin
  847. case ch of
  848. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{':
  849. Result := True
  850. else
  851. Result := False
  852. end;
  853. end;
  854. function _IsMetaSymbol2(ch: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  855. begin
  856. case ch of
  857. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{',
  858. ']', '}':
  859. Result := True
  860. else
  861. Result := False
  862. end;
  863. end;
  864. function QuoteRegExprMetaChars(const AStr: RegExprString): RegExprString;
  865. var
  866. i, i0, Len: integer;
  867. ch: REChar;
  868. begin
  869. Result := '';
  870. Len := Length(AStr);
  871. i := 1;
  872. i0 := i;
  873. while i <= Len do
  874. begin
  875. ch := AStr[i];
  876. if _IsMetaSymbol2(ch) then
  877. begin
  878. Result := Result + System.Copy(AStr, i0, i - i0) + EscChar + ch;
  879. i0 := i + 1;
  880. end;
  881. Inc(i);
  882. end;
  883. Result := Result + System.Copy(AStr, i0, MaxInt); // Tail
  884. end; { of function QuoteRegExprMetaChars
  885. -------------------------------------------------------------- }
  886. function RegExprSubExpressions(const ARegExpr: string; ASubExprs: TStrings;
  887. AExtendedSyntax: boolean = False): integer;
  888. type
  889. TStackItemRec = record // ###0.945
  890. SubExprIdx: integer;
  891. StartPos: PtrInt;
  892. end;
  893. TStackArray = packed array [0 .. NSUBEXPMAX - 1] of TStackItemRec;
  894. var
  895. Len, SubExprLen: integer;
  896. i, i0: integer;
  897. Modif: TRegExprModifiers;
  898. Stack: ^TStackArray; // ###0.945
  899. StackIdx, StackSz: integer;
  900. begin
  901. Result := 0; // no unbalanced brackets found at this very moment
  902. Modif:=Default(TRegExprModifiers);
  903. ASubExprs.Clear; // I don't think that adding to non empty list
  904. // can be useful, so I simplified algorithm to work only with empty list
  905. Len := Length(ARegExpr); // some optimization tricks
  906. // first we have to calculate number of subexpression to reserve
  907. // space in Stack array (may be we'll reserve more than needed, but
  908. // it's faster then memory reallocation during parsing)
  909. StackSz := 1; // add 1 for entire r.e.
  910. for i := 1 to Len do
  911. if ARegExpr[i] = '(' then
  912. Inc(StackSz);
  913. // SetLength (Stack, StackSz); //###0.945
  914. GetMem(Stack, SizeOf(TStackItemRec) * StackSz);
  915. try
  916. StackIdx := 0;
  917. i := 1;
  918. while (i <= Len) do
  919. begin
  920. case ARegExpr[i] of
  921. '(':
  922. begin
  923. if (i < Len) and (ARegExpr[i + 1] = '?') then
  924. begin
  925. // this is not subexpression, but comment or other
  926. // Perl extension. We must check is it (?ismxrg-ismxrg)
  927. // and change AExtendedSyntax if /x is changed.
  928. Inc(i, 2); // skip '(?'
  929. i0 := i;
  930. while (i <= Len) and (ARegExpr[i] <> ')') do
  931. Inc(i);
  932. if i > Len then
  933. Result := -1 // unbalansed '('
  934. else
  935. if ParseModifiers(@ARegExpr[i0], i - i0, Modif) then
  936. // Alexey-T: original code had copy from i, not from i0
  937. AExtendedSyntax := Modif.X;
  938. end
  939. else
  940. begin // subexpression starts
  941. ASubExprs.Add(''); // just reserve space
  942. with Stack[StackIdx] do
  943. begin
  944. SubExprIdx := ASubExprs.Count - 1;
  945. StartPos := i;
  946. end;
  947. Inc(StackIdx);
  948. end;
  949. end;
  950. ')':
  951. begin
  952. if StackIdx = 0 then
  953. Result := i // unbalanced ')'
  954. else
  955. begin
  956. Dec(StackIdx);
  957. with Stack[StackIdx] do
  958. begin
  959. SubExprLen := i - StartPos + 1;
  960. ASubExprs.Objects[SubExprIdx] :=
  961. TObject(StartPos or (SubExprLen ShL 16));
  962. ASubExprs[SubExprIdx] := System.Copy(ARegExpr, StartPos + 1,
  963. SubExprLen - 2); // add without brackets
  964. end;
  965. end;
  966. end;
  967. EscChar:
  968. Inc(i); // skip quoted symbol
  969. '[':
  970. begin
  971. // we have to skip character ranges at once, because they can
  972. // contain '#', and '#' in it must NOT be recognized as eXtended
  973. // comment beginning!
  974. i0 := i;
  975. Inc(i);
  976. if ARegExpr[i] = ']' // first ']' inside [] treated as simple char, no need to check '['
  977. then
  978. Inc(i);
  979. while (i <= Len) and (ARegExpr[i] <> ']') do
  980. if ARegExpr[i] = EscChar // ###0.942
  981. then
  982. Inc(i, 2) // skip 'escaped' char to prevent stopping at '\]'
  983. else
  984. Inc(i);
  985. if (i > Len) or (ARegExpr[i] <> ']') // ###0.942
  986. then
  987. Result := -(i0 + 1); // unbalansed '[' //###0.942
  988. end;
  989. '#':
  990. if AExtendedSyntax then
  991. begin
  992. // skip eXtended comments
  993. while (i <= Len) and (ARegExpr[i] <> #$d) and (ARegExpr[i] <> #$a)
  994. // do not use [#$d, #$a] due to UniCode compatibility
  995. do
  996. Inc(i);
  997. while (i + 1 <= Len) and
  998. ((ARegExpr[i + 1] = #$d) or (ARegExpr[i + 1] = #$a)) do
  999. Inc(i); // attempt to work with different kinds of line separators
  1000. // now we are at the line separator that must be skipped.
  1001. end;
  1002. // here is no 'else' clause - we simply skip ordinary chars
  1003. end; // of case
  1004. Inc(i); // skip scanned char
  1005. // ! can move after Len due to skipping quoted symbol
  1006. end;
  1007. // check brackets balance
  1008. if StackIdx <> 0 then
  1009. Result := -1; // unbalansed '('
  1010. // check if entire r.e. added
  1011. if (ASubExprs.Count = 0) or ((PtrInt(ASubExprs.Objects[0]) and $FFFF) <> 1)
  1012. or (((PtrInt(ASubExprs.Objects[0]) ShR 16) and $FFFF) <> Len)
  1013. // whole r.e. wasn't added because it isn't bracketed
  1014. // well, we add it now:
  1015. then
  1016. ASubExprs.InsertObject(0, ARegExpr, TObject((Len ShL 16) or 1));
  1017. finally
  1018. FreeMem(Stack);
  1019. end;
  1020. end; { of function RegExprSubExpressions
  1021. -------------------------------------------------------------- }
  1022. const
  1023. OP_MAGIC = TREOp(216); // programm signature
  1024. // name opcode opnd? meaning
  1025. OP_EEND = TREOp(0); // - End of program
  1026. OP_BOL = TREOp(1); // - Match "" at beginning of line
  1027. OP_EOL = TREOp(2); // - Match "" at end of line
  1028. OP_ANY = TREOp(3); // - Match any one character
  1029. OP_ANYOF = TREOp(4); // Str Match any character in string Str
  1030. OP_ANYBUT = TREOp(5); // Str Match any char. not in string Str
  1031. OP_BRANCH = TREOp(6); // Node Match this alternative, or the next
  1032. OP_BACK = TREOp(7); // - Jump backward (Next < 0)
  1033. OP_EXACTLY = TREOp(8); // Str Match string Str
  1034. OP_NOTHING = TREOp(9); // - Match empty string
  1035. OP_STAR = TREOp(10); // Node Match this (simple) thing 0 or more times
  1036. OP_PLUS = TREOp(11); // Node Match this (simple) thing 1 or more times
  1037. OP_ANYDIGIT = TREOp(12); // - Match any digit (equiv [0-9])
  1038. OP_NOTDIGIT = TREOp(13); // - Match not digit (equiv [0-9])
  1039. OP_ANYLETTER = TREOp(14); // - Match any letter from property WordChars
  1040. OP_NOTLETTER = TREOp(15); // - Match not letter from property WordChars
  1041. OP_ANYSPACE = TREOp(16); // - Match any space char (see property SpaceChars)
  1042. OP_NOTSPACE = TREOp(17); // - Match not space char (see property SpaceChars)
  1043. OP_BRACES = TREOp(18);
  1044. // Node,Min,Max Match this (simple) thing from Min to Max times.
  1045. // Min and Max are TREBracesArg
  1046. OP_COMMENT = TREOp(19); // - Comment ;)
  1047. OP_EXACTLYCI = TREOp(20); // Str Match string Str case insensitive
  1048. OP_ANYOFCI = TREOp(21);
  1049. // Str Match any character in string Str, case insensitive
  1050. OP_ANYBUTCI = TREOp(22);
  1051. // Str Match any char. not in string Str, case insensitive
  1052. OP_LOOPENTRY = TREOp(23); // Node Start of loop (Node - LOOP for this loop)
  1053. OP_LOOP = TREOp(24); // Node,Min,Max,LoopEntryJmp - back jump for LOOPENTRY.
  1054. // Min and Max are TREBracesArg
  1055. // Node - next node in sequence,
  1056. // LoopEntryJmp - associated LOOPENTRY node addr
  1057. OP_BSUBEXP = TREOp(28);
  1058. // Idx Match previously matched subexpression #Idx (stored as REChar) //###0.936
  1059. OP_BSUBEXPCI = TREOp(29); // Idx -"- in case-insensitive mode
  1060. // Non-Greedy Style Ops //###0.940
  1061. OP_STARNG = TREOp(30); // Same as OP_START but in non-greedy mode
  1062. OP_PLUSNG = TREOp(31); // Same as OP_PLUS but in non-greedy mode
  1063. OP_BRACESNG = TREOp(32); // Same as OP_BRACES but in non-greedy mode
  1064. OP_LOOPNG = TREOp(33); // Same as OP_LOOP but in non-greedy mode
  1065. // Multiline mode \m
  1066. OP_BOLML = TREOp(34); // - Match "" at beginning of line
  1067. OP_EOLML = TREOp(35); // - Match "" at end of line
  1068. OP_ANYML = TREOp(36); // - Match any one character
  1069. // Word boundary
  1070. OP_BOUND = TREOp(37); // Match "" between words //###0.943
  1071. OP_NOTBOUND = TREOp(38); // Match "" not between words //###0.943
  1072. OP_ANYHORZSEP = TREOp(39); // Any horizontal whitespace \h
  1073. OP_NOTHORZSEP = TREOp(40); // Not horizontal whitespace \H
  1074. OP_ANYVERTSEP = TREOp(41); // Any vertical whitespace \v
  1075. OP_NOTVERTSEP = TREOp(42); // Not vertical whitespace \V
  1076. // !!! Change OP_OPEN value if you add new opcodes !!!
  1077. OP_OPEN = TREOp(43); // - Mark this point in input as start of \n
  1078. // OP_OPEN + 1 is \1, etc.
  1079. OP_CLOSE = TREOp(Ord(OP_OPEN) + NSUBEXP);
  1080. // - Analogous to OP_OPEN.
  1081. // !!! Don't add new OpCodes after CLOSE !!!
  1082. // We work with p-code through pointers, compatible with PRegExprChar.
  1083. // Note: all code components (TRENextOff, TREOp, TREBracesArg, etc)
  1084. // must have lengths that can be divided by SizeOf (REChar) !
  1085. // A node is TREOp of opcode followed Next "pointer" of TRENextOff type.
  1086. // The Next is a offset from the opcode of the node containing it.
  1087. // An operand, if any, simply follows the node. (Note that much of
  1088. // the code generation knows about this implicit relationship!)
  1089. // Using TRENextOff=PtrInt speed up p-code processing.
  1090. // Opcodes description:
  1091. //
  1092. // BRANCH The set of branches constituting a single choice are hooked
  1093. // together with their "next" pointers, since precedence prevents
  1094. // anything being concatenated to any individual branch. The
  1095. // "next" pointer of the last BRANCH in a choice points to the
  1096. // thing following the whole choice. This is also where the
  1097. // final "next" pointer of each individual branch points; each
  1098. // branch starts with the operand node of a BRANCH node.
  1099. // BACK Normal "next" pointers all implicitly point forward; BACK
  1100. // exists to make loop structures possible.
  1101. // STAR,PLUS,BRACES '?', and complex '*' and '+', are implemented as
  1102. // circular BRANCH structures using BACK. Complex '{min,max}'
  1103. // - as pair LOOPENTRY-LOOP (see below). Simple cases (one
  1104. // character per match) are implemented with STAR, PLUS and
  1105. // BRACES for speed and to minimize recursive plunges.
  1106. // LOOPENTRY,LOOP {min,max} are implemented as special pair
  1107. // LOOPENTRY-LOOP. Each LOOPENTRY initialize loopstack for
  1108. // current level.
  1109. // OPEN,CLOSE are numbered at compile time.
  1110. { ============================================================= }
  1111. { ================== Error handling section =================== }
  1112. { ============================================================= }
  1113. const
  1114. reeOk = 0;
  1115. reeCompNullArgument = 100;
  1116. reeCompParseRegTooManyBrackets = 102;
  1117. reeCompParseRegUnmatchedBrackets = 103;
  1118. reeCompParseRegUnmatchedBrackets2 = 104;
  1119. reeCompParseRegJunkOnEnd = 105;
  1120. reePlusStarOperandCouldBeEmpty = 106;
  1121. reeNestedSQP = 107;
  1122. reeBadHexDigit = 108;
  1123. reeInvalidRange = 109;
  1124. reeParseAtomTrailingBackSlash = 110;
  1125. reeNoHexCodeAfterBSlashX = 111;
  1126. reeHexCodeAfterBSlashXTooBig = 112;
  1127. reeUnmatchedSqBrackets = 113;
  1128. reeInternalUrp = 114;
  1129. reeQPSBFollowsNothing = 115;
  1130. reeTrailingBackSlash = 116;
  1131. reeNoLetterAfterBSlashC = 117;
  1132. reeMetaCharAfterMinusInRange = 118;
  1133. reeRarseAtomInternalDisaster = 119;
  1134. reeIncorrectBraces = 121;
  1135. reeBRACESArgTooBig = 122;
  1136. reeUnknownOpcodeInFillFirst = 123;
  1137. reeBracesMinParamGreaterMax = 124;
  1138. reeUnclosedComment = 125;
  1139. reeComplexBracesNotImplemented = 126;
  1140. reeUnrecognizedModifier = 127;
  1141. reeBadLinePairedSeparator = 128;
  1142. // Runtime errors must be >= 1000
  1143. reeRegRepeatCalledInappropriately = 1000;
  1144. reeMatchPrimMemoryCorruption = 1001;
  1145. reeMatchPrimCorruptedPointers = 1002;
  1146. reeNoExpression = 1003;
  1147. reeCorruptedProgram = 1004;
  1148. reeNoInputStringSpecified = 1005;
  1149. reeOffsetMustBePositive = 1006;
  1150. reeExecNextWithoutExec = 1007;
  1151. reeBadOpcodeInCharClass = 1008;
  1152. reeDumpCorruptedOpcode = 1011;
  1153. reeModifierUnsupported = 1013;
  1154. reeLoopStackExceeded = 1014;
  1155. reeLoopWithoutEntry = 1015;
  1156. function TRegExpr.ErrorMsg(AErrorID: integer): RegExprString;
  1157. begin
  1158. case AErrorID of
  1159. reeOk:
  1160. Result := 'No errors';
  1161. reeCompNullArgument:
  1162. Result := 'TRegExpr compile: null argument';
  1163. reeCompParseRegTooManyBrackets:
  1164. Result := 'TRegExpr compile: ParseReg: too many ()';
  1165. reeCompParseRegUnmatchedBrackets:
  1166. Result := 'TRegExpr compile: ParseReg: unmatched ()';
  1167. reeCompParseRegUnmatchedBrackets2:
  1168. Result := 'TRegExpr compile: ParseReg: unmatched ()';
  1169. reeCompParseRegJunkOnEnd:
  1170. Result := 'TRegExpr compile: ParseReg: junk at end';
  1171. reePlusStarOperandCouldBeEmpty:
  1172. Result := 'TRegExpr compile: *+ operand could be empty';
  1173. reeNestedSQP:
  1174. Result := 'TRegExpr compile: nested *?+';
  1175. reeBadHexDigit:
  1176. Result := 'TRegExpr compile: bad hex digit';
  1177. reeInvalidRange:
  1178. Result := 'TRegExpr compile: invalid [] range';
  1179. reeParseAtomTrailingBackSlash:
  1180. Result := 'TRegExpr compile: parse atom trailing \';
  1181. reeNoHexCodeAfterBSlashX:
  1182. Result := 'TRegExpr compile: no hex code after \x';
  1183. reeNoLetterAfterBSlashC:
  1184. Result := 'TRegExpr compile: no letter "A".."Z" after \c';
  1185. reeMetaCharAfterMinusInRange:
  1186. Result := 'TRegExpr compile: metachar after "-" in [] range';
  1187. reeHexCodeAfterBSlashXTooBig:
  1188. Result := 'TRegExpr compile: hex code after \x is too big';
  1189. reeUnmatchedSqBrackets:
  1190. Result := 'TRegExpr compile: unmatched []';
  1191. reeInternalUrp:
  1192. Result := 'TRegExpr compile: internal fail on char "|", ")"';
  1193. reeQPSBFollowsNothing:
  1194. Result := 'TRegExpr compile: ?+*{ follows nothing';
  1195. reeTrailingBackSlash:
  1196. Result := 'TRegExpr compile: trailing \';
  1197. reeRarseAtomInternalDisaster:
  1198. Result := 'TRegExpr compile: RarseAtom internal disaster';
  1199. reeIncorrectBraces:
  1200. Result := 'TRegExpr compile: incorrect {} braces';
  1201. reeBRACESArgTooBig:
  1202. Result := 'TRegExpr compile: braces {} argument too big';
  1203. reeUnknownOpcodeInFillFirst:
  1204. Result := 'TRegExpr compile: unknown opcode in FillFirstCharSet ('+DumpOp(fLastErrorOpcode)+')';
  1205. reeBracesMinParamGreaterMax:
  1206. Result := 'TRegExpr compile: braces {} min param greater then max';
  1207. reeUnclosedComment:
  1208. Result := 'TRegExpr compile: unclosed (?#comment)';
  1209. reeComplexBracesNotImplemented:
  1210. Result := 'TRegExpr compile: if you use braces {} and non-greedy ops *?, +?, ?? for complex cases, enable {$DEFINE ComplexBraces}';
  1211. reeUnrecognizedModifier:
  1212. Result := 'TRegExpr compile: unrecognized modifier';
  1213. reeBadLinePairedSeparator:
  1214. Result := 'TRegExpr compile: LinePairedSeparator must countain two different chars or be empty';
  1215. reeRegRepeatCalledInappropriately:
  1216. Result := 'TRegExpr exec: RegRepeat called inappropriately';
  1217. reeMatchPrimMemoryCorruption:
  1218. Result := 'TRegExpr exec: MatchPrim memory corruption';
  1219. reeMatchPrimCorruptedPointers:
  1220. Result := 'TRegExpr exec: MatchPrim corrupted pointers';
  1221. reeNoExpression:
  1222. Result := 'TRegExpr exec: empty expression';
  1223. reeCorruptedProgram:
  1224. Result := 'TRegExpr exec: corrupted opcode (no magic byte)';
  1225. reeNoInputStringSpecified:
  1226. Result := 'TRegExpr exec: empty input string';
  1227. reeOffsetMustBePositive:
  1228. Result := 'TRegExpr exec: offset must be >0';
  1229. reeExecNextWithoutExec:
  1230. Result := 'TRegExpr exec: ExecNext without Exec(Pos)';
  1231. reeBadOpcodeInCharClass:
  1232. Result := 'TRegExpr exec: invalid opcode in char class';
  1233. reeDumpCorruptedOpcode:
  1234. Result := 'TRegExpr dump: corrupted opcode';
  1235. reeLoopStackExceeded:
  1236. Result := 'TRegExpr exec: loop stack exceeded';
  1237. reeLoopWithoutEntry:
  1238. Result := 'TRegExpr exec: loop without loop entry';
  1239. else
  1240. Result := 'Unknown error';
  1241. end;
  1242. end; { of procedure TRegExpr.Error
  1243. -------------------------------------------------------------- }
  1244. function TRegExpr.LastError: integer;
  1245. begin
  1246. Result := fLastError;
  1247. fLastError := reeOk;
  1248. end; { of function TRegExpr.LastError
  1249. -------------------------------------------------------------- }
  1250. { ============================================================= }
  1251. { ===================== Common section ======================== }
  1252. { ============================================================= }
  1253. class function TRegExpr.VersionMajor: integer;
  1254. begin
  1255. Result := REVersionMajor;
  1256. end;
  1257. class function TRegExpr.VersionMinor: integer;
  1258. begin
  1259. Result := REVersionMinor;
  1260. end;
  1261. constructor TRegExpr.Create;
  1262. begin
  1263. inherited;
  1264. programm := nil;
  1265. fExpression := '';
  1266. fInputString := '';
  1267. FEmptyInputRaisesError := False;
  1268. regexpBegin := nil;
  1269. regexpIsCompiled := False;
  1270. FillChar(fModifiers, SIzeOf(fModifiers), 0);
  1271. ModifierI := RegExprModifierI;
  1272. ModifierR := RegExprModifierR;
  1273. ModifierS := RegExprModifierS;
  1274. ModifierG := RegExprModifierG;
  1275. ModifierM := RegExprModifierM;
  1276. ModifierX := RegExprModifierX;
  1277. SpaceChars := RegExprSpaceChars; // ###0.927
  1278. WordChars := RegExprWordChars; // ###0.929
  1279. fInvertCase := RegExprInvertCaseFunction; // ###0.927
  1280. fLineSeparators := RegExprLineSeparators; // ###0.941
  1281. LinePairedSeparator := RegExprLinePairedSeparator; // ###0.941
  1282. FUseOsLineEndOnReplace := True;
  1283. FReplaceLineEnd := sLineBreak;
  1284. {$IFDEF UnicodeWordDetection}
  1285. FUseUnicodeWordDetection := True;
  1286. {$ENDIF}
  1287. fSlowChecksSizeMax := 2000;
  1288. InitLineSepArray;
  1289. InitCharCheckers;
  1290. end; { of constructor TRegExpr.Create
  1291. -------------------------------------------------------------- }
  1292. constructor TRegExpr.Create(const AExpression: RegExprString);
  1293. begin
  1294. Create;
  1295. Expression := AExpression;
  1296. end;
  1297. destructor TRegExpr.Destroy;
  1298. begin
  1299. if programm <> nil then
  1300. begin
  1301. FreeMem(programm);
  1302. programm := nil;
  1303. end;
  1304. end; { of destructor TRegExpr.Destroy
  1305. -------------------------------------------------------------- }
  1306. class function TRegExpr.InvertCaseFunction(const Ch: REChar): REChar;
  1307. begin
  1308. Result := Ch;
  1309. if (Ch >= 'a') and (Ch <= 'z') then
  1310. begin
  1311. Dec(Result, 32);
  1312. Exit;
  1313. end;
  1314. if (Ch >= 'A') and (Ch <= 'Z') then
  1315. begin
  1316. Inc(Result, 32);
  1317. Exit;
  1318. end;
  1319. if Ord(Ch) < 128 then
  1320. Exit;
  1321. Result := _UpperCase(Ch);
  1322. if Result = Ch then
  1323. Result := _LowerCase(Ch);
  1324. Result := _UpperCase(Ch);
  1325. if Result = Ch then
  1326. Result := _LowerCase(Ch);
  1327. end; { of function TRegExpr.InvertCaseFunction
  1328. -------------------------------------------------------------- }
  1329. procedure TRegExpr.SetExpression(const AStr: RegExprString);
  1330. begin
  1331. if (AStr <> fExpression) or not regexpIsCompiled then
  1332. begin
  1333. regexpIsCompiled := False;
  1334. fExpression := AStr;
  1335. UniqueString(fExpression);
  1336. fRegexStart := PRegExprChar(fExpression);
  1337. fRegexEnd := fRegexStart + Length(fExpression);
  1338. InvalidateProgramm; // ###0.941
  1339. end;
  1340. end; { of procedure TRegExpr.SetExpression
  1341. -------------------------------------------------------------- }
  1342. function TRegExpr.GetSubExprCount: integer;
  1343. begin
  1344. // if nothing found, we must return -1 per TRegExpr docs
  1345. if startp[0] = nil then
  1346. Result := -1
  1347. else
  1348. Result := GrpCount;
  1349. end;
  1350. function TRegExpr.GetMatchPos(Idx: integer): PtrInt;
  1351. begin
  1352. Idx := GrpIndexes[Idx];
  1353. if (Idx >= 0) and (startp[Idx] <> nil) then
  1354. Result := startp[Idx] - fInputStart + 1
  1355. else
  1356. Result := -1;
  1357. end; { of function TRegExpr.GetMatchPos
  1358. -------------------------------------------------------------- }
  1359. function TRegExpr.GetMatchLen(Idx: integer): PtrInt;
  1360. begin
  1361. Idx := GrpIndexes[Idx];
  1362. if (Idx >= 0) and (startp[Idx] <> nil) then
  1363. Result := endp[Idx] - startp[Idx]
  1364. else
  1365. Result := -1;
  1366. end; { of function TRegExpr.GetMatchLen
  1367. -------------------------------------------------------------- }
  1368. function TRegExpr.GetMatch(Idx: integer): RegExprString;
  1369. begin
  1370. Result := '';
  1371. Idx := GrpIndexes[Idx];
  1372. if (Idx >= 0) and (endp[Idx] > startp[Idx]) then
  1373. SetString(Result, startp[Idx], endp[Idx] - startp[Idx]);
  1374. {
  1375. // then Result := copy (fInputString, MatchPos [Idx], MatchLen [Idx]) //###0.929
  1376. then
  1377. begin
  1378. SetLength(Result, endp[Idx] - startp[Idx]);
  1379. System.Move(startp[Idx]^, Result[1], Length(Result) * SizeOf(REChar));
  1380. end;
  1381. }
  1382. end; { of function TRegExpr.GetMatch
  1383. -------------------------------------------------------------- }
  1384. function TRegExpr.GetModifierStr: RegExprString;
  1385. begin
  1386. Result := '-';
  1387. if ModifierI then
  1388. Result := 'i' + Result
  1389. else
  1390. Result := Result + 'i';
  1391. if ModifierR then
  1392. Result := 'r' + Result
  1393. else
  1394. Result := Result + 'r';
  1395. if ModifierS then
  1396. Result := 's' + Result
  1397. else
  1398. Result := Result + 's';
  1399. if ModifierG then
  1400. Result := 'g' + Result
  1401. else
  1402. Result := Result + 'g';
  1403. if ModifierM then
  1404. Result := 'm' + Result
  1405. else
  1406. Result := Result + 'm';
  1407. if ModifierX then
  1408. Result := 'x' + Result
  1409. else
  1410. Result := Result + 'x';
  1411. if Result[Length(Result)] = '-' // remove '-' if all modifiers are 'On'
  1412. then
  1413. System.Delete(Result, Length(Result), 1);
  1414. end; { of function TRegExpr.GetModifierStr
  1415. -------------------------------------------------------------- }
  1416. procedure TRegExpr.SetModifierG(AValue: boolean);
  1417. begin
  1418. if fModifiers.G <> AValue then
  1419. begin
  1420. fModifiers.G := AValue;
  1421. InvalidateProgramm;
  1422. end;
  1423. end;
  1424. procedure TRegExpr.SetModifierI(AValue: boolean);
  1425. begin
  1426. if fModifiers.I <> AValue then
  1427. begin
  1428. fModifiers.I := AValue;
  1429. InvalidateProgramm;
  1430. end;
  1431. end;
  1432. procedure TRegExpr.SetModifierM(AValue: boolean);
  1433. begin
  1434. if fModifiers.M <> AValue then
  1435. begin
  1436. fModifiers.M := AValue;
  1437. InvalidateProgramm;
  1438. end;
  1439. end;
  1440. procedure TRegExpr.SetModifierR(AValue: boolean);
  1441. begin
  1442. if fModifiers.R <> AValue then
  1443. begin
  1444. fModifiers.R := AValue;
  1445. InvalidateProgramm;
  1446. end;
  1447. end;
  1448. procedure TRegExpr.SetModifierS(AValue: boolean);
  1449. begin
  1450. if fModifiers.S <> AValue then
  1451. begin
  1452. fModifiers.S := AValue;
  1453. InvalidateProgramm;
  1454. end;
  1455. end;
  1456. procedure TRegExpr.SetModifierX(AValue: boolean);
  1457. begin
  1458. if fModifiers.X <> AValue then
  1459. begin
  1460. fModifiers.X := AValue;
  1461. InvalidateProgramm;
  1462. end;
  1463. end;
  1464. procedure TRegExpr.SetModifierStr(const AStr: RegExprString);
  1465. begin
  1466. if ParseModifiers(PRegExprChar(AStr), Length(AStr), fModifiers) then
  1467. InvalidateProgramm
  1468. else
  1469. Error(reeModifierUnsupported);
  1470. end;
  1471. { ============================================================= }
  1472. { ==================== Compiler section ======================= }
  1473. { ============================================================= }
  1474. {$IFDEF UnicodeWordDetection}
  1475. {$IFDEF FPC}
  1476. function IsUnicodeWordChar(AChar: WideChar): boolean; inline;
  1477. var
  1478. NType: byte;
  1479. begin
  1480. if Ord(AChar) >= LOW_SURROGATE_BEGIN then
  1481. Exit(False);
  1482. NType := GetProps(Ord(AChar))^.Category;
  1483. Result := (NType <= UGC_OtherNumber);
  1484. end;
  1485. {$ELSE}
  1486. function IsUnicodeWordChar(AChar: WideChar): boolean; inline;
  1487. begin
  1488. Result := System.Character.IsLetterOrDigit(AChar);
  1489. end;
  1490. {$ENDIF}
  1491. {$ENDIF}
  1492. function TRegExpr.IsWordChar(AChar: REChar): boolean;
  1493. begin
  1494. Result := Pos(AChar, fWordChars) > 0;
  1495. {$IFDEF UnicodeWordDetection}
  1496. if not Result and (Ord(AChar) >= 128) and UseUnicodeWordDetection then
  1497. Result := IsUnicodeWordChar(AChar);
  1498. {$ENDIF}
  1499. end;
  1500. function TRegExpr.IsSpaceChar(AChar: REChar): boolean;
  1501. begin
  1502. Result := Pos(AChar, fSpaceChars) > 0;
  1503. end;
  1504. function TRegExpr.IsCustomLineSeparator(AChar: REChar): boolean;
  1505. begin
  1506. {$IFDEF UniCode}
  1507. Result := Pos(AChar, fLineSeparators) > 0;
  1508. {$ELSE}
  1509. Result := fLineSepArray[byte(AChar)];
  1510. {$ENDIF}
  1511. end;
  1512. function IsDigitChar(AChar: REChar): boolean; inline;
  1513. begin
  1514. case AChar of
  1515. '0' .. '9':
  1516. Result := True;
  1517. else
  1518. Result := False;
  1519. end;
  1520. end;
  1521. function IsHorzSeparator(AChar: REChar): boolean; inline;
  1522. begin
  1523. // Tab and Unicode categoty "Space Separator": https://www.compart.com/en/unicode/category/Zs
  1524. case AChar of
  1525. #9, #$20, #$A0:
  1526. Result := True;
  1527. {$IFDEF UniCode}
  1528. #$1680, #$2000 .. #$200A, #$202F, #$205F, #$3000:
  1529. Result := True;
  1530. {$ENDIF}
  1531. else
  1532. Result := False;
  1533. end;
  1534. end;
  1535. function IsLineSeparator(AChar: REChar): boolean; inline;
  1536. begin
  1537. case AChar of
  1538. #$d, #$a, #$b, #$c:
  1539. Result := True;
  1540. {$IFDEF UniCode}
  1541. #$2028, #$2029, #$85:
  1542. Result := True;
  1543. {$ENDIF}
  1544. else
  1545. Result := False;
  1546. end;
  1547. end;
  1548. procedure TRegExpr.InvalidateProgramm;
  1549. begin
  1550. if programm <> nil then
  1551. begin
  1552. FreeMem(programm);
  1553. programm := nil;
  1554. end;
  1555. end; { of procedure TRegExpr.InvalidateProgramm
  1556. -------------------------------------------------------------- }
  1557. procedure TRegExpr.Compile;
  1558. begin
  1559. if fExpression = '' then
  1560. begin
  1561. Error(reeNoExpression);
  1562. Exit;
  1563. end;
  1564. CompileRegExpr(PRegExprChar(fExpression));
  1565. end; { of procedure TRegExpr.Compile
  1566. -------------------------------------------------------------- }
  1567. procedure TRegExpr.InitLineSepArray;
  1568. {$IFNDEF UniCode}
  1569. var
  1570. i: integer;
  1571. {$ENDIF}
  1572. begin
  1573. {$IFNDEF UniCode}
  1574. FillChar(fLineSepArray, SizeOf(fLineSepArray), 0);
  1575. for i := 1 to Length(fLineSeparators) do
  1576. fLineSepArray[byte(fLineSeparators[i])] := True;
  1577. {$ENDIF}
  1578. end;
  1579. function TRegExpr.IsProgrammOk: boolean;
  1580. begin
  1581. Result := False;
  1582. // check modifiers
  1583. if not IsModifiersEqual(fModifiers, fProgModifiers) // ###0.941
  1584. then
  1585. InvalidateProgramm;
  1586. // [Re]compile if needed
  1587. if programm = nil then
  1588. begin
  1589. Compile; // ###0.941
  1590. // Check [re]compiled programm
  1591. if programm = nil then
  1592. Exit; // error was set/raised by Compile (was reeExecAfterCompErr)
  1593. end;
  1594. if programm[0] <> OP_MAGIC // Program corrupted.
  1595. then
  1596. Error(reeCorruptedProgram)
  1597. else
  1598. Result := True;
  1599. end; { of function TRegExpr.IsProgrammOk
  1600. -------------------------------------------------------------- }
  1601. procedure TRegExpr.Tail(p: PRegExprChar; val: PRegExprChar);
  1602. // set the next-pointer at the end of a node chain
  1603. var
  1604. scan: PRegExprChar;
  1605. temp: PRegExprChar;
  1606. begin
  1607. if p = @regdummy then
  1608. Exit;
  1609. // Find last node.
  1610. scan := p;
  1611. repeat
  1612. temp := regnext(scan);
  1613. if temp = nil then
  1614. Break;
  1615. scan := temp;
  1616. until False;
  1617. // Set Next 'pointer'
  1618. if val < scan then
  1619. PRENextOff(AlignToPtr(scan + REOpSz))^ := -(scan - val) // ###0.948
  1620. // work around PWideChar subtraction bug (Delphi uses
  1621. // shr after subtraction to calculate widechar distance %-( )
  1622. // so, if difference is negative we have .. the "feature" :(
  1623. // I could wrap it in $IFDEF UniCode, but I didn't because
  1624. // "P – Q computes the difference between the address given
  1625. // by P (the higher address) and the address given by Q (the
  1626. // lower address)" - Delphi help quotation.
  1627. else
  1628. PRENextOff(AlignToPtr(scan + REOpSz))^ := val - scan; // ###0.933
  1629. end; { of procedure TRegExpr.Tail
  1630. -------------------------------------------------------------- }
  1631. procedure TRegExpr.OpTail(p: PRegExprChar; val: PRegExprChar);
  1632. // regtail on operand of first argument; nop if operandless
  1633. begin
  1634. // "Operandless" and "op != OP_BRANCH" are synonymous in practice.
  1635. if (p = nil) or (p = @regdummy) or (PREOp(p)^ <> OP_BRANCH) then
  1636. Exit;
  1637. Tail(p + REOpSz + RENextOffSz, val); // ###0.933
  1638. end; { of procedure TRegExpr.OpTail
  1639. -------------------------------------------------------------- }
  1640. function TRegExpr.EmitNode(op: TREOp): PRegExprChar; // ###0.933
  1641. // emit a node, return location
  1642. begin
  1643. Result := regcode;
  1644. if Result <> @regdummy then
  1645. begin
  1646. PREOp(regcode)^ := op;
  1647. Inc(regcode, REOpSz);
  1648. PRENextOff(AlignToPtr(regcode))^ := 0; // Next "pointer" := nil
  1649. Inc(regcode, RENextOffSz);
  1650. if (op = OP_EXACTLY) or (op = OP_EXACTLYCI) then
  1651. regExactlyLen := PLongInt(regcode)
  1652. else
  1653. regExactlyLen := nil;
  1654. {$IFDEF DebugSynRegExpr}
  1655. if regcode - programm > regsize then
  1656. raise Exception.Create('TRegExpr.EmitNode buffer overrun');
  1657. {$ENDIF}
  1658. end
  1659. else
  1660. Inc(regsize, REOpSz + RENextOffSz);
  1661. // compute code size without code generation
  1662. end; { of function TRegExpr.EmitNode
  1663. -------------------------------------------------------------- }
  1664. procedure TRegExpr.EmitC(ch: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  1665. begin
  1666. if regcode <> @regdummy then
  1667. begin
  1668. regcode^ := ch;
  1669. Inc(regcode);
  1670. {$IFDEF DebugSynRegExpr}
  1671. if regcode - programm > regsize then
  1672. raise Exception.Create('TRegExpr.EmitC buffer overrun');
  1673. {$ENDIF}
  1674. end
  1675. else
  1676. Inc(regsize, REOpSz); // Type of p-code pointer always is ^REChar
  1677. end; { of procedure TRegExpr.EmitC
  1678. -------------------------------------------------------------- }
  1679. procedure TRegExpr.EmitInt(AValue: LongInt); {$IFDEF InlineFuncs}inline;{$ENDIF}
  1680. begin
  1681. if regcode <> @regdummy then
  1682. begin
  1683. PLongInt(regcode)^ := AValue;
  1684. Inc(regcode, RENumberSz);
  1685. {$IFDEF DebugSynRegExpr}
  1686. if regcode - programm > regsize then
  1687. raise Exception.Create('TRegExpr.EmitInt buffer overrun');
  1688. {$ENDIF}
  1689. end
  1690. else
  1691. Inc(regsize, RENumberSz);
  1692. end;
  1693. procedure TRegExpr.InsertOperator(op: TREOp; opnd: PRegExprChar; sz: integer);
  1694. // insert an operator in front of already-emitted operand
  1695. // Means relocating the operand.
  1696. var
  1697. src, dst, place: PRegExprChar;
  1698. i: integer;
  1699. begin
  1700. if regcode = @regdummy then
  1701. begin
  1702. Inc(regsize, sz);
  1703. Exit;
  1704. end;
  1705. // move code behind insert position
  1706. src := regcode;
  1707. Inc(regcode, sz);
  1708. {$IFDEF DebugSynRegExpr}
  1709. if regcode - programm > regsize then
  1710. raise Exception.Create('TRegExpr.InsertOperator buffer overrun');
  1711. // if (opnd<regcode) or (opnd-regcode>regsize) then
  1712. // raise Exception.Create('TRegExpr.InsertOperator invalid opnd');
  1713. {$ENDIF}
  1714. dst := regcode;
  1715. while src > opnd do
  1716. begin
  1717. Dec(dst);
  1718. Dec(src);
  1719. dst^ := src^;
  1720. end;
  1721. place := opnd; // Op node, where operand used to be.
  1722. PREOp(place)^ := op;
  1723. Inc(place, REOpSz);
  1724. for i := 1 + REOpSz to sz do
  1725. begin
  1726. place^ := #0;
  1727. Inc(place);
  1728. end;
  1729. end; { of procedure TRegExpr.InsertOperator
  1730. -------------------------------------------------------------- }
  1731. function FindSkippedMetaLen(PStart, PEnd: PRegExprChar): integer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1732. // find length of initial segment of PStart string consisting
  1733. // entirely of characters not from IsMetaSymbol1.
  1734. begin
  1735. Result := 0;
  1736. while PStart < PEnd do
  1737. begin
  1738. if _IsMetaSymbol1(PStart^) then
  1739. Exit;
  1740. Inc(Result);
  1741. Inc(PStart)
  1742. end;
  1743. end;
  1744. const
  1745. // Flags to be passed up and down.
  1746. flag_HasWidth = 01; // Known never to match nil string.
  1747. flag_Simple = 02; // Simple enough to be OP_STAR/OP_PLUS/OP_BRACES operand.
  1748. flag_SpecStart = 04; // Starts with * or +.
  1749. flag_Worst = 0; // Worst case.
  1750. {$IFDEF UniCode}
  1751. RusRangeLoLow = #$430; // 'а'
  1752. RusRangeLoHigh = #$44F; // 'я'
  1753. RusRangeHiLow = #$410; // 'А'
  1754. RusRangeHiHigh = #$42F; // 'Я'
  1755. {$ELSE}
  1756. RusRangeLoLow = #$E0; // 'а' in cp1251
  1757. RusRangeLoHigh = #$FF; // 'я' in cp1251
  1758. RusRangeHiLow = #$C0; // 'А' in cp1251
  1759. RusRangeHiHigh = #$DF; // 'Я' in cp1251
  1760. {$ENDIF}
  1761. function TRegExpr.FindInCharClass(ABuffer: PRegExprChar; AChar: REChar; AIgnoreCase: boolean): boolean;
  1762. // Buffer contains char pairs: (Kind, Data), where Kind is one of OpKind_ values,
  1763. // and Data depends on Kind
  1764. var
  1765. ch, ch2: REChar;
  1766. N, i: integer;
  1767. begin
  1768. if AIgnoreCase then
  1769. AChar := _UpperCase(AChar);
  1770. repeat
  1771. case ABuffer^ of
  1772. OpKind_End:
  1773. begin
  1774. Result := False;
  1775. Exit;
  1776. end;
  1777. OpKind_Range:
  1778. begin
  1779. Inc(ABuffer);
  1780. ch := ABuffer^;
  1781. Inc(ABuffer);
  1782. ch2 := ABuffer^;
  1783. Inc(ABuffer);
  1784. {
  1785. // if AIgnoreCase, ch, ch2 are upcased in opcode
  1786. if AIgnoreCase then
  1787. begin
  1788. ch := _UpperCase(ch);
  1789. ch2 := _UpperCase(ch2);
  1790. end;
  1791. }
  1792. if (AChar >= ch) and (AChar <= ch2) then
  1793. begin
  1794. Result := True;
  1795. Exit;
  1796. end;
  1797. end;
  1798. OpKind_MetaClass:
  1799. begin
  1800. Inc(ABuffer);
  1801. N := Ord(ABuffer^);
  1802. Inc(ABuffer);
  1803. if CharCheckers[N](AChar) then
  1804. begin
  1805. Result := True;
  1806. Exit
  1807. end;
  1808. end;
  1809. OpKind_Char:
  1810. begin
  1811. Inc(ABuffer);
  1812. N := PLongInt(ABuffer)^;
  1813. Inc(ABuffer, RENumberSz);
  1814. for i := 1 to N do
  1815. begin
  1816. ch := ABuffer^;
  1817. Inc(ABuffer);
  1818. {
  1819. // already upcased in opcode
  1820. if AIgnoreCase then
  1821. ch := _UpperCase(ch);
  1822. }
  1823. if ch = AChar then
  1824. begin
  1825. Result := True;
  1826. Exit;
  1827. end;
  1828. end;
  1829. end;
  1830. else
  1831. Error(reeBadOpcodeInCharClass);
  1832. end;
  1833. until False; // assume that Buffer is ended correctly
  1834. end;
  1835. procedure TRegExpr.GetCharSetFromWordChars(var ARes: TRegExprCharset);
  1836. var
  1837. i: integer;
  1838. ch: REChar;
  1839. begin
  1840. ARes := [];
  1841. for i := 1 to Length(fWordChars) do
  1842. begin
  1843. ch := fWordChars[i];
  1844. {$IFDEF UniCode}
  1845. if Ord(ch) <= $FF then
  1846. {$ENDIF}
  1847. Include(ARes, byte(ch));
  1848. end;
  1849. end;
  1850. procedure TRegExpr.GetCharSetFromSpaceChars(var ARes: TRegExprCharset);
  1851. var
  1852. i: integer;
  1853. ch: REChar;
  1854. begin
  1855. ARes := [];
  1856. for i := 1 to Length(fSpaceChars) do
  1857. begin
  1858. ch := fSpaceChars[i];
  1859. {$IFDEF UniCode}
  1860. if Ord(ch) <= $FF then
  1861. {$ENDIF}
  1862. Include(ARes, byte(ch));
  1863. end;
  1864. end;
  1865. procedure TRegExpr.GetCharSetFromCharClass(ABuffer: PRegExprChar; AIgnoreCase: boolean; var ARes: TRegExprCharset);
  1866. var
  1867. ch, ch2: REChar;
  1868. TempSet: TRegExprCharSet;
  1869. N, i: integer;
  1870. begin
  1871. ARes := [];
  1872. TempSet := [];
  1873. repeat
  1874. case ABuffer^ of
  1875. OpKind_End:
  1876. Exit;
  1877. OpKind_Range:
  1878. begin
  1879. Inc(ABuffer);
  1880. ch := ABuffer^;
  1881. Inc(ABuffer);
  1882. ch2 := ABuffer^;
  1883. Inc(ABuffer);
  1884. for i := Ord(ch) to
  1885. {$IFDEF UniCode} Min(Ord(ch2), $FF) {$ELSE} Ord(ch2) {$ENDIF} do
  1886. begin
  1887. Include(ARes, byte(i));
  1888. if AIgnoreCase then
  1889. Include(ARes, byte(InvertCase(REChar(i))));
  1890. end;
  1891. end;
  1892. OpKind_MetaClass:
  1893. begin
  1894. Inc(ABuffer);
  1895. N := Ord(ABuffer^);
  1896. Inc(ABuffer);
  1897. if N = CheckerIndex_Word then
  1898. begin
  1899. GetCharSetFromWordChars(TempSet);
  1900. ARes := ARes + TempSet;
  1901. end
  1902. else
  1903. if N = CheckerIndex_NotWord then
  1904. begin
  1905. GetCharSetFromWordChars(TempSet);
  1906. ARes := ARes + (RegExprAllSet - TempSet);
  1907. end
  1908. else
  1909. if N = CheckerIndex_Space then
  1910. begin
  1911. GetCharSetFromSpaceChars(TempSet);
  1912. ARes := ARes + TempSet;
  1913. end
  1914. else
  1915. if N = CheckerIndex_NotSpace then
  1916. begin
  1917. GetCharSetFromSpaceChars(TempSet);
  1918. ARes := ARes + (RegExprAllSet - TempSet);
  1919. end
  1920. else
  1921. if N = CheckerIndex_Digit then
  1922. ARes := ARes + RegExprDigitSet
  1923. else
  1924. if N = CheckerIndex_NotDigit then
  1925. ARes := ARes + (RegExprAllSet - RegExprDigitSet)
  1926. else
  1927. if N = CheckerIndex_VertSep then
  1928. ARes := ARes + RegExprLineSeparatorsSet
  1929. else
  1930. if N = CheckerIndex_NotVertSep then
  1931. ARes := ARes + (RegExprAllSet - RegExprLineSeparatorsSet)
  1932. else
  1933. if N = CheckerIndex_HorzSep then
  1934. ARes := ARes + RegExprHorzSeparatorsSet
  1935. else
  1936. if N = CheckerIndex_NotHorzSep then
  1937. ARes := ARes + (RegExprAllSet - RegExprHorzSeparatorsSet)
  1938. else
  1939. if N = CheckerIndex_LowerAZ then
  1940. begin
  1941. if AIgnoreCase then
  1942. ARes := ARes + RegExprAllAzSet
  1943. else
  1944. ARes := ARes + RegExprLowerAzSet;
  1945. end
  1946. else
  1947. if N = CheckerIndex_UpperAZ then
  1948. begin
  1949. if AIgnoreCase then
  1950. ARes := ARes + RegExprAllAzSet
  1951. else
  1952. ARes := ARes + RegExprUpperAzSet;
  1953. end
  1954. else
  1955. Error(reeBadOpcodeInCharClass);
  1956. end;
  1957. OpKind_Char:
  1958. begin
  1959. Inc(ABuffer);
  1960. N := PLongInt(ABuffer)^;
  1961. Inc(ABuffer, RENumberSz);
  1962. for i := 1 to N do
  1963. begin
  1964. ch := ABuffer^;
  1965. Inc(ABuffer);
  1966. {$IFDEF UniCode}
  1967. if Ord(ch) <= $FF then
  1968. {$ENDIF}
  1969. begin
  1970. Include(ARes, byte(ch));
  1971. if AIgnoreCase then
  1972. Include(ARes, byte(InvertCase(ch)));
  1973. end;
  1974. end;
  1975. end;
  1976. else
  1977. Error(reeBadOpcodeInCharClass);
  1978. end;
  1979. until False; // assume that Buffer is ended correctly
  1980. end;
  1981. function TRegExpr.GetModifierG: boolean;
  1982. begin
  1983. Result := fModifiers.G;
  1984. end;
  1985. function TRegExpr.GetModifierI: boolean;
  1986. begin
  1987. Result := fModifiers.I;
  1988. end;
  1989. function TRegExpr.GetModifierM: boolean;
  1990. begin
  1991. Result := fModifiers.M;
  1992. end;
  1993. function TRegExpr.GetModifierR: boolean;
  1994. begin
  1995. Result := fModifiers.R;
  1996. end;
  1997. function TRegExpr.GetModifierS: boolean;
  1998. begin
  1999. Result := fModifiers.S;
  2000. end;
  2001. function TRegExpr.GetModifierX: boolean;
  2002. begin
  2003. Result := fModifiers.X;
  2004. end;
  2005. function TRegExpr.CompileRegExpr(ARegExp: PRegExprChar): boolean;
  2006. // Compile a regular expression into internal code
  2007. // We can't allocate space until we know how big the compiled form will be,
  2008. // but we can't compile it (and thus know how big it is) until we've got a
  2009. // place to put the code. So we cheat: we compile it twice, once with code
  2010. // generation turned off and size counting turned on, and once "for real".
  2011. // This also means that we don't allocate space until we are sure that the
  2012. // thing really will compile successfully, and we never have to move the
  2013. // code and thus invalidate pointers into it. (Note that it has to be in
  2014. // one piece because free() must be able to free it all.)
  2015. // Beware that the optimization-preparation code in here knows about some
  2016. // of the structure of the compiled regexp.
  2017. var
  2018. scan, longest, longestTemp: PRegExprChar;
  2019. Len, LenTemp: integer;
  2020. flags: integer;
  2021. begin
  2022. Result := False; // life too dark
  2023. flags := 0;
  2024. regparse := nil; // for correct error handling
  2025. regexpBegin := ARegExp;
  2026. regExactlyLen := nil;
  2027. ClearInternalIndexes;
  2028. fLastError := reeOk;
  2029. fLastErrorOpcode := TREOp(0);
  2030. try
  2031. if programm <> nil then
  2032. begin
  2033. FreeMem(programm);
  2034. programm := nil;
  2035. end;
  2036. if ARegExp = nil then
  2037. begin
  2038. Error(reeCompNullArgument);
  2039. Exit;
  2040. end;
  2041. fProgModifiers := fModifiers;
  2042. // well, may it's paranoia. I'll check it later... !!!!!!!!
  2043. // First pass: determine size, legality.
  2044. fSecondPass := False;
  2045. fCompModifiers := fModifiers;
  2046. regparse := ARegExp;
  2047. regnpar := 1;
  2048. regsize := 0;
  2049. regcode := @regdummy;
  2050. EmitC(OP_MAGIC);
  2051. if ParseReg(0, flags) = nil then
  2052. Exit;
  2053. // Allocate space.
  2054. GetMem(programm, regsize * SizeOf(REChar));
  2055. // Second pass: emit code.
  2056. fSecondPass := True;
  2057. fCompModifiers := fModifiers;
  2058. regparse := ARegExp;
  2059. regnpar := 1;
  2060. regcode := programm;
  2061. EmitC(OP_MAGIC);
  2062. if ParseReg(0, flags) = nil then
  2063. Exit;
  2064. // Dig out information for optimizations.
  2065. {$IFDEF UseFirstCharSet} // ###0.929
  2066. FirstCharSet := [];
  2067. FillFirstCharSet(programm + REOpSz);
  2068. for Len := 0 to 255 do
  2069. FirstCharArray[Len] := byte(Len) in FirstCharSet;
  2070. {$ENDIF}
  2071. reganchored := #0;
  2072. regmust := nil;
  2073. regmustlen := 0;
  2074. regmustString := '';
  2075. scan := programm + REOpSz; // First OP_BRANCH.
  2076. if PREOp(regnext(scan))^ = OP_EEND then
  2077. begin // Only one top-level choice.
  2078. scan := scan + REOpSz + RENextOffSz;
  2079. // Starting-point info.
  2080. if PREOp(scan)^ = OP_BOL then
  2081. Inc(reganchored);
  2082. // If there's something expensive in the r.e., find the longest
  2083. // literal string that must appear and make it the regmust. Resolve
  2084. // ties in favor of later strings, since the regstart check works
  2085. // with the beginning of the r.e. and avoiding duplication
  2086. // strengthens checking. Not a strong reason, but sufficient in the
  2087. // absence of others.
  2088. if (flags and flag_SpecStart) <> 0 then
  2089. begin
  2090. longest := nil;
  2091. Len := 0;
  2092. while scan <> nil do
  2093. begin
  2094. if PREOp(scan)^ = OP_EXACTLY then
  2095. begin
  2096. longestTemp := scan + REOpSz + RENextOffSz + RENumberSz;
  2097. LenTemp := PLongInt(scan + REOpSz + RENextOffSz)^;
  2098. if LenTemp >= Len then
  2099. begin
  2100. longest := longestTemp;
  2101. Len := LenTemp;
  2102. end;
  2103. end;
  2104. scan := regnext(scan);
  2105. end;
  2106. regmust := longest;
  2107. regmustlen := Len;
  2108. if regmustlen > 1 then // don't use regmust if too short
  2109. SetString(regmustString, regmust, regmustlen);
  2110. end;
  2111. end;
  2112. Result := True;
  2113. finally
  2114. begin
  2115. if not Result then
  2116. InvalidateProgramm;
  2117. regexpBegin := nil;
  2118. regexpIsCompiled := Result; // ###0.944
  2119. end;
  2120. end;
  2121. end; { of function TRegExpr.CompileRegExpr
  2122. -------------------------------------------------------------- }
  2123. procedure TRegExpr.SetUseOsLineEndOnReplace(AValue: boolean);
  2124. begin
  2125. if FUseOsLineEndOnReplace = AValue then
  2126. Exit;
  2127. FUseOsLineEndOnReplace := AValue;
  2128. if FUseOsLineEndOnReplace then
  2129. FReplaceLineEnd := sLineBreak
  2130. else
  2131. FReplaceLineEnd := #10;
  2132. end;
  2133. function TRegExpr.ParseReg(paren: integer; var flagp: integer): PRegExprChar;
  2134. // regular expression, i.e. main body or parenthesized thing
  2135. // Caller must absorb opening parenthesis.
  2136. // Combining parenthesis handling with the base level of regular expression
  2137. // is a trifle forced, but the need to tie the tails of the branches to what
  2138. // follows makes it hard to avoid.
  2139. var
  2140. ret, br, ender: PRegExprChar;
  2141. parno: integer;
  2142. flags: integer;
  2143. SavedModifiers: TRegExprModifiers;
  2144. begin
  2145. flags := 0;
  2146. Result := nil;
  2147. flagp := flag_HasWidth; // Tentatively.
  2148. parno := 0; // eliminate compiler stupid warning
  2149. SavedModifiers := fCompModifiers;
  2150. // Make an OP_OPEN node, if parenthesized.
  2151. if paren <> 0 then
  2152. begin
  2153. if regnpar >= NSUBEXP then
  2154. begin
  2155. Error(reeCompParseRegTooManyBrackets);
  2156. Exit;
  2157. end;
  2158. parno := regnpar;
  2159. Inc(regnpar);
  2160. ret := EmitNode(TREOp(Ord(OP_OPEN) + parno));
  2161. end
  2162. else
  2163. ret := nil;
  2164. // Pick up the branches, linking them together.
  2165. br := ParseBranch(flags);
  2166. if br = nil then
  2167. begin
  2168. Result := nil;
  2169. Exit;
  2170. end;
  2171. if ret <> nil then
  2172. Tail(ret, br) // OP_OPEN -> first.
  2173. else
  2174. ret := br;
  2175. if (flags and flag_HasWidth) = 0 then
  2176. flagp := flagp and not flag_HasWidth;
  2177. flagp := flagp or flags and flag_SpecStart;
  2178. while (regparse^ = '|') do
  2179. begin
  2180. Inc(regparse);
  2181. br := ParseBranch(flags);
  2182. if br = nil then
  2183. begin
  2184. Result := nil;
  2185. Exit;
  2186. end;
  2187. Tail(ret, br); // OP_BRANCH -> OP_BRANCH.
  2188. if (flags and flag_HasWidth) = 0 then
  2189. flagp := flagp and not flag_HasWidth;
  2190. flagp := flagp or flags and flag_SpecStart;
  2191. end;
  2192. // Make a closing node, and hook it on the end.
  2193. if paren <> 0 then
  2194. ender := EmitNode(TREOp(Ord(OP_CLOSE) + parno))
  2195. else
  2196. ender := EmitNode(OP_EEND);
  2197. Tail(ret, ender);
  2198. // Hook the tails of the branches to the closing node.
  2199. br := ret;
  2200. while br <> nil do
  2201. begin
  2202. OpTail(br, ender);
  2203. br := regnext(br);
  2204. end;
  2205. // Check for proper termination.
  2206. if paren <> 0 then
  2207. if regparse^ <> ')' then
  2208. begin
  2209. Error(reeCompParseRegUnmatchedBrackets);
  2210. Exit;
  2211. end
  2212. else
  2213. Inc(regparse); // skip trailing ')'
  2214. if (paren = 0) and (regparse < fRegexEnd) then
  2215. begin
  2216. if regparse^ = ')' then
  2217. Error(reeCompParseRegUnmatchedBrackets2)
  2218. else
  2219. Error(reeCompParseRegJunkOnEnd);
  2220. Exit;
  2221. end;
  2222. fCompModifiers := SavedModifiers; // restore modifiers of parent
  2223. Result := ret;
  2224. end; { of function TRegExpr.ParseReg
  2225. -------------------------------------------------------------- }
  2226. function TRegExpr.ParseBranch(var flagp: integer): PRegExprChar;
  2227. // one alternative of an | operator
  2228. // Implements the concatenation operator.
  2229. var
  2230. ret, chain, latest: PRegExprChar;
  2231. flags: integer;
  2232. begin
  2233. flags := 0;
  2234. flagp := flag_Worst; // Tentatively.
  2235. ret := EmitNode(OP_BRANCH);
  2236. chain := nil;
  2237. while (regparse < fRegexEnd) and (regparse^ <> '|') and (regparse^ <> ')') do
  2238. begin
  2239. latest := ParsePiece(flags);
  2240. if latest = nil then
  2241. begin
  2242. Result := nil;
  2243. Exit;
  2244. end;
  2245. flagp := flagp or flags and flag_HasWidth;
  2246. if chain = nil // First piece.
  2247. then
  2248. flagp := flagp or flags and flag_SpecStart
  2249. else
  2250. Tail(chain, latest);
  2251. chain := latest;
  2252. end;
  2253. if chain = nil // Loop ran zero times.
  2254. then
  2255. EmitNode(OP_NOTHING);
  2256. Result := ret;
  2257. end; { of function TRegExpr.ParseBranch
  2258. -------------------------------------------------------------- }
  2259. function TRegExpr.ParsePiece(var flagp: integer): PRegExprChar;
  2260. // something followed by possible [*+?{]
  2261. // Note that the branching code sequences used for ? and the general cases
  2262. // of * and + and { are somewhat optimized: they use the same OP_NOTHING node as
  2263. // both the endmarker for their branch list and the body of the last branch.
  2264. // It might seem that this node could be dispensed with entirely, but the
  2265. // endmarker role is not redundant.
  2266. function ParseNumber(AStart, AEnd: PRegExprChar): TREBracesArg;
  2267. begin
  2268. Result := 0;
  2269. if AEnd - AStart + 1 > 8 then
  2270. begin // prevent stupid scanning
  2271. Error(reeBRACESArgTooBig);
  2272. Exit;
  2273. end;
  2274. while AStart <= AEnd do
  2275. begin
  2276. Result := Result * 10 + (Ord(AStart^) - Ord('0'));
  2277. Inc(AStart);
  2278. end;
  2279. if (Result > MaxBracesArg) or (Result < 0) then
  2280. begin
  2281. Error(reeBRACESArgTooBig);
  2282. Exit;
  2283. end;
  2284. end;
  2285. var
  2286. TheOp: TREOp;
  2287. NextNode: PRegExprChar;
  2288. procedure EmitComplexBraces(ABracesMin, ABracesMax: TREBracesArg; ANonGreedyOp: boolean); // ###0.940
  2289. {$IFDEF ComplexBraces}
  2290. var
  2291. off: TRENextOff;
  2292. {$ENDIF}
  2293. begin
  2294. {$IFNDEF ComplexBraces}
  2295. Error(reeComplexBracesNotImplemented);
  2296. {$ELSE}
  2297. if ANonGreedyOp then
  2298. TheOp := OP_LOOPNG
  2299. else
  2300. TheOp := OP_LOOP;
  2301. InsertOperator(OP_LOOPENTRY, Result, REOpSz + RENextOffSz);
  2302. NextNode := EmitNode(TheOp);
  2303. if regcode <> @regdummy then
  2304. begin
  2305. off := (Result + REOpSz + RENextOffSz) - (regcode - REOpSz - RENextOffSz);
  2306. // back to Atom after OP_LOOPENTRY
  2307. PREBracesArg(AlignToInt(regcode))^ := ABracesMin;
  2308. Inc(regcode, REBracesArgSz);
  2309. PREBracesArg(AlignToInt(regcode))^ := ABracesMax;
  2310. Inc(regcode, REBracesArgSz);
  2311. PRENextOff(AlignToPtr(regcode))^ := off;
  2312. Inc(regcode, RENextOffSz);
  2313. {$IFDEF DebugSynRegExpr}
  2314. if regcode - programm > regsize then
  2315. raise Exception.Create
  2316. ('TRegExpr.ParsePiece.EmitComplexBraces buffer overrun');
  2317. {$ENDIF}
  2318. end
  2319. else
  2320. Inc(regsize, REBracesArgSz * 2 + RENextOffSz);
  2321. Tail(Result, NextNode); // OP_LOOPENTRY -> OP_LOOP
  2322. if regcode <> @regdummy then
  2323. Tail(Result + REOpSz + RENextOffSz, NextNode); // Atom -> OP_LOOP
  2324. {$ENDIF}
  2325. end;
  2326. procedure EmitSimpleBraces(ABracesMin, ABracesMax: TREBracesArg; ANonGreedyOp: boolean); // ###0.940
  2327. begin
  2328. if ANonGreedyOp // ###0.940
  2329. then
  2330. TheOp := OP_BRACESNG
  2331. else
  2332. TheOp := OP_BRACES;
  2333. InsertOperator(TheOp, Result, REOpSz + RENextOffSz + REBracesArgSz * 2);
  2334. if regcode <> @regdummy then
  2335. begin
  2336. PREBracesArg(AlignToInt(Result + REOpSz + RENextOffSz))^ := ABracesMin;
  2337. PREBracesArg(AlignToInt(Result + REOpSz + RENextOffSz + REBracesArgSz))^ := ABracesMax;
  2338. end;
  2339. end;
  2340. var
  2341. op: REChar;
  2342. NonGreedyOp, NonGreedyCh: boolean; // ###0.940
  2343. flags: integer;
  2344. BracesMin, Bracesmax: TREBracesArg;
  2345. p: PRegExprChar;
  2346. begin
  2347. flags := 0;
  2348. Result := ParseAtom(flags);
  2349. if Result = nil then
  2350. Exit;
  2351. op := regparse^;
  2352. if not ((op = '*') or (op = '+') or (op = '?') or (op = '{')) then
  2353. begin
  2354. flagp := flags;
  2355. Exit;
  2356. end;
  2357. if ((flags and flag_HasWidth) = 0) and (op <> '?') then
  2358. begin
  2359. Error(reePlusStarOperandCouldBeEmpty);
  2360. Exit;
  2361. end;
  2362. case op of
  2363. '*':
  2364. begin
  2365. flagp := flag_Worst or flag_SpecStart;
  2366. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2367. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2368. // ###0.940
  2369. if (flags and flag_Simple) = 0 then
  2370. begin
  2371. if NonGreedyOp // ###0.940
  2372. then
  2373. EmitComplexBraces(0, MaxBracesArg, NonGreedyOp)
  2374. else
  2375. begin // Emit x* as (x&|), where & means "self".
  2376. InsertOperator(OP_BRANCH, Result, REOpSz + RENextOffSz); // Either x
  2377. OpTail(Result, EmitNode(OP_BACK)); // and loop
  2378. OpTail(Result, Result); // back
  2379. Tail(Result, EmitNode(OP_BRANCH)); // or
  2380. Tail(Result, EmitNode(OP_NOTHING)); // nil.
  2381. end
  2382. end
  2383. else
  2384. begin // Simple
  2385. if NonGreedyOp // ###0.940
  2386. then
  2387. TheOp := OP_STARNG
  2388. else
  2389. TheOp := OP_STAR;
  2390. InsertOperator(TheOp, Result, REOpSz + RENextOffSz);
  2391. end;
  2392. if NonGreedyCh // ###0.940
  2393. then
  2394. Inc(regparse); // Skip extra char ('?')
  2395. end; { of case '*' }
  2396. '+':
  2397. begin
  2398. flagp := flag_Worst or flag_SpecStart or flag_HasWidth;
  2399. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2400. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2401. // ###0.940
  2402. if (flags and flag_Simple) = 0 then
  2403. begin
  2404. if NonGreedyOp // ###0.940
  2405. then
  2406. EmitComplexBraces(1, MaxBracesArg, NonGreedyOp)
  2407. else
  2408. begin // Emit x+ as x(&|), where & means "self".
  2409. NextNode := EmitNode(OP_BRANCH); // Either
  2410. Tail(Result, NextNode);
  2411. Tail(EmitNode(OP_BACK), Result); // loop back
  2412. Tail(NextNode, EmitNode(OP_BRANCH)); // or
  2413. Tail(Result, EmitNode(OP_NOTHING)); // nil.
  2414. end
  2415. end
  2416. else
  2417. begin // Simple
  2418. if NonGreedyOp // ###0.940
  2419. then
  2420. TheOp := OP_PLUSNG
  2421. else
  2422. TheOp := OP_PLUS;
  2423. InsertOperator(TheOp, Result, REOpSz + RENextOffSz);
  2424. end;
  2425. if NonGreedyCh // ###0.940
  2426. then
  2427. Inc(regparse); // Skip extra char ('?')
  2428. end; { of case '+' }
  2429. '?':
  2430. begin
  2431. flagp := flag_Worst;
  2432. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2433. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2434. // ###0.940
  2435. if NonGreedyOp then
  2436. begin // ###0.940 // We emit x?? as x{0,1}?
  2437. if (flags and flag_Simple) = 0 then
  2438. EmitComplexBraces(0, 1, NonGreedyOp)
  2439. else
  2440. EmitSimpleBraces(0, 1, NonGreedyOp);
  2441. end
  2442. else
  2443. begin // greedy '?'
  2444. InsertOperator(OP_BRANCH, Result, REOpSz + RENextOffSz); // Either x
  2445. Tail(Result, EmitNode(OP_BRANCH)); // or
  2446. NextNode := EmitNode(OP_NOTHING); // nil.
  2447. Tail(Result, NextNode);
  2448. OpTail(Result, NextNode);
  2449. end;
  2450. if NonGreedyCh // ###0.940
  2451. then
  2452. Inc(regparse); // Skip extra char ('?')
  2453. end; { of case '?' }
  2454. '{':
  2455. begin
  2456. Inc(regparse);
  2457. p := regparse;
  2458. while IsDigitChar(regparse^) do // <min> MUST appear
  2459. Inc(regparse);
  2460. if (regparse^ <> '}') and (regparse^ <> ',') or (p = regparse) then
  2461. begin
  2462. Error(reeIncorrectBraces);
  2463. Exit;
  2464. end;
  2465. BracesMin := ParseNumber(p, regparse - 1);
  2466. if regparse^ = ',' then
  2467. begin
  2468. Inc(regparse);
  2469. p := regparse;
  2470. while IsDigitChar(regparse^) do
  2471. Inc(regparse);
  2472. if regparse^ <> '}' then
  2473. begin
  2474. Error(reeIncorrectBraces);
  2475. Exit;
  2476. end;
  2477. if p = regparse then
  2478. Bracesmax := MaxBracesArg
  2479. else
  2480. Bracesmax := ParseNumber(p, regparse - 1);
  2481. end
  2482. else
  2483. Bracesmax := BracesMin; // {n} == {n,n}
  2484. if BracesMin > Bracesmax then
  2485. begin
  2486. Error(reeBracesMinParamGreaterMax);
  2487. Exit;
  2488. end;
  2489. if BracesMin > 0 then
  2490. flagp := flag_Worst;
  2491. if Bracesmax > 0 then
  2492. flagp := flagp or flag_HasWidth or flag_SpecStart;
  2493. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2494. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2495. // ###0.940
  2496. if (flags and flag_Simple) <> 0 then
  2497. EmitSimpleBraces(BracesMin, Bracesmax, NonGreedyOp)
  2498. else
  2499. EmitComplexBraces(BracesMin, Bracesmax, NonGreedyOp);
  2500. if NonGreedyCh // ###0.940
  2501. then
  2502. Inc(regparse); // Skip extra char '?'
  2503. end; // of case '{'
  2504. // else // here we can't be
  2505. end; { of case op }
  2506. Inc(regparse);
  2507. op := regparse^;
  2508. if (op = '*') or (op = '+') or (op = '?') or (op = '{') then
  2509. Error(reeNestedSQP);
  2510. end; { of function TRegExpr.ParsePiece
  2511. -------------------------------------------------------------- }
  2512. function TRegExpr.HexDig(Ch: REChar): integer;
  2513. begin
  2514. case Ch of
  2515. '0' .. '9':
  2516. Result := Ord(Ch) - Ord('0');
  2517. 'a' .. 'f':
  2518. Result := Ord(Ch) - Ord('a') + 10;
  2519. 'A' .. 'F':
  2520. Result := Ord(Ch) - Ord('A') + 10;
  2521. else
  2522. begin
  2523. Result := 0;
  2524. Error(reeBadHexDigit);
  2525. end;
  2526. end;
  2527. end;
  2528. function TRegExpr.UnQuoteChar(var APtr: PRegExprChar): REChar;
  2529. var
  2530. Ch: REChar;
  2531. begin
  2532. case APtr^ of
  2533. 't':
  2534. Result := #$9; // \t => tab (HT/TAB)
  2535. 'n':
  2536. Result := #$a; // \n => newline (NL)
  2537. 'r':
  2538. Result := #$d; // \r => carriage return (CR)
  2539. 'f':
  2540. Result := #$c; // \f => form feed (FF)
  2541. 'a':
  2542. Result := #$7; // \a => alarm (bell) (BEL)
  2543. 'e':
  2544. Result := #$1b; // \e => escape (ESC)
  2545. 'c':
  2546. begin // \cK => code for Ctrl+K
  2547. Inc(APtr);
  2548. if APtr >= fRegexEnd then
  2549. Error(reeNoLetterAfterBSlashC);
  2550. Ch := APtr^;
  2551. case Ch of
  2552. 'a' .. 'z':
  2553. Result := REChar(Ord(Ch) - Ord('a') + 1);
  2554. 'A' .. 'Z':
  2555. Result := REChar(Ord(Ch) - Ord('A') + 1);
  2556. else
  2557. Error(reeNoLetterAfterBSlashC);
  2558. end;
  2559. end;
  2560. 'x':
  2561. begin // \x: hex char
  2562. Result := #0;
  2563. Inc(APtr);
  2564. if APtr >= fRegexEnd then
  2565. begin
  2566. Error(reeNoHexCodeAfterBSlashX);
  2567. Exit;
  2568. end;
  2569. if APtr^ = '{' then
  2570. begin // \x{nnnn} //###0.936
  2571. repeat
  2572. Inc(APtr);
  2573. if APtr >= fRegexEnd then
  2574. begin
  2575. Error(reeNoHexCodeAfterBSlashX);
  2576. Exit;
  2577. end;
  2578. if APtr^ <> '}' then
  2579. begin
  2580. if (Ord(Result) ShR (SizeOf(REChar) * 8 - 4)) and $F <> 0 then
  2581. begin
  2582. Error(reeHexCodeAfterBSlashXTooBig);
  2583. Exit;
  2584. end;
  2585. Result := REChar((Ord(Result) ShL 4) or HexDig(APtr^));
  2586. // HexDig will cause Error if bad hex digit found
  2587. end
  2588. else
  2589. Break;
  2590. until False;
  2591. end
  2592. else
  2593. begin
  2594. Result := REChar(HexDig(APtr^));
  2595. // HexDig will cause Error if bad hex digit found
  2596. Inc(APtr);
  2597. if APtr >= fRegexEnd then
  2598. begin
  2599. Error(reeNoHexCodeAfterBSlashX);
  2600. Exit;
  2601. end;
  2602. Result := REChar((Ord(Result) ShL 4) or HexDig(APtr^));
  2603. // HexDig will cause Error if bad hex digit found
  2604. end;
  2605. end;
  2606. else
  2607. Result := APtr^;
  2608. end;
  2609. end;
  2610. function TRegExpr.ParseAtom(var flagp: integer): PRegExprChar;
  2611. // the lowest level
  2612. // Optimization: gobbles an entire sequence of ordinary characters so that
  2613. // it can turn them into a single node, which is smaller to store and
  2614. // faster to run. Backslashed characters are exceptions, each becoming a
  2615. // separate node; the code is simpler that way and it's not worth fixing.
  2616. var
  2617. ret: PRegExprChar;
  2618. RangeBeg, RangeEnd: REChar;
  2619. CanBeRange: boolean;
  2620. AddrOfLen: PLongInt;
  2621. procedure EmitExactly(Ch: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  2622. begin
  2623. if fCompModifiers.I then
  2624. ret := EmitNode(OP_EXACTLYCI)
  2625. else
  2626. ret := EmitNode(OP_EXACTLY);
  2627. EmitInt(1);
  2628. EmitC(Ch);
  2629. flagp := flagp or flag_HasWidth or flag_Simple;
  2630. end;
  2631. procedure EmitRangeChar(Ch: REChar; AStartOfRange: boolean); {$IFDEF InlineFuncs}inline;{$ENDIF}
  2632. begin
  2633. CanBeRange := AStartOfRange;
  2634. if fCompModifiers.I then
  2635. Ch := _UpperCase(Ch);
  2636. if AStartOfRange then
  2637. begin
  2638. AddrOfLen := nil;
  2639. RangeBeg := Ch;
  2640. end
  2641. else
  2642. begin
  2643. if AddrOfLen = nil then
  2644. begin
  2645. EmitC(OpKind_Char);
  2646. Pointer(AddrOfLen) := regcode;
  2647. EmitInt(0);
  2648. end;
  2649. Inc(AddrOfLen^);
  2650. EmitC(Ch);
  2651. end;
  2652. end;
  2653. procedure EmitRangePacked(ch1, ch2: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  2654. var
  2655. ChkIndex: integer;
  2656. begin
  2657. AddrOfLen := nil;
  2658. CanBeRange := False;
  2659. if fCompModifiers.I then
  2660. begin
  2661. ch1 := _UpperCase(ch1);
  2662. ch2 := _UpperCase(ch2);
  2663. end;
  2664. for ChkIndex := Low(CharCheckerInfos) to High(CharCheckerInfos) do
  2665. if (CharCheckerInfos[ChkIndex].CharBegin = ch1) and
  2666. (CharCheckerInfos[ChkIndex].CharEnd = ch2) then
  2667. begin
  2668. EmitC(OpKind_MetaClass);
  2669. EmitC(REChar(CharCheckerInfos[ChkIndex].CheckerIndex));
  2670. Exit;
  2671. end;
  2672. EmitC(OpKind_Range);
  2673. EmitC(ch1);
  2674. EmitC(ch2);
  2675. end;
  2676. var
  2677. flags: integer;
  2678. Len: integer;
  2679. SavedPtr: PRegExprChar;
  2680. EnderChar, TempChar: REChar;
  2681. DashForRange: Boolean;
  2682. begin
  2683. Result := nil;
  2684. flags := 0;
  2685. flagp := flag_Worst;
  2686. AddrOfLen := nil;
  2687. Inc(regparse);
  2688. case (regparse - 1)^ of
  2689. '^':
  2690. if not fCompModifiers.M or
  2691. ((fLineSeparators = '') and not fLinePairedSeparatorAssigned) then
  2692. ret := EmitNode(OP_BOL)
  2693. else
  2694. ret := EmitNode(OP_BOLML);
  2695. '$':
  2696. if not fCompModifiers.M or
  2697. ((fLineSeparators = '') and not fLinePairedSeparatorAssigned) then
  2698. ret := EmitNode(OP_EOL)
  2699. else
  2700. ret := EmitNode(OP_EOLML);
  2701. '.':
  2702. if fCompModifiers.S then
  2703. begin
  2704. ret := EmitNode(OP_ANY);
  2705. flagp := flagp or flag_HasWidth or flag_Simple;
  2706. end
  2707. else
  2708. begin // not /s, so emit [^:LineSeparators:]
  2709. ret := EmitNode(OP_ANYML);
  2710. flagp := flagp or flag_HasWidth; // not so simple ;)
  2711. end;
  2712. '[':
  2713. begin
  2714. if regparse^ = '^' then
  2715. begin // Complement of range.
  2716. if fCompModifiers.I then
  2717. ret := EmitNode(OP_ANYBUTCI)
  2718. else
  2719. ret := EmitNode(OP_ANYBUT);
  2720. Inc(regparse);
  2721. end
  2722. else if fCompModifiers.I then
  2723. ret := EmitNode(OP_ANYOFCI)
  2724. else
  2725. ret := EmitNode(OP_ANYOF);
  2726. CanBeRange := False;
  2727. if regparse^ = ']' then
  2728. begin
  2729. // first ']' inside [] treated as simple char, no need to check '['
  2730. EmitRangeChar(regparse^, (regparse + 1)^ = '-');
  2731. Inc(regparse);
  2732. end;
  2733. while (regparse < fRegexEnd) and (regparse^ <> ']') do
  2734. begin
  2735. // last '-' inside [] treated as simple dash
  2736. if (regparse^ = '-') and
  2737. ((regparse + 1) < fRegexEnd) and
  2738. ((regparse + 1)^ = ']') then
  2739. begin
  2740. EmitRangeChar('-', False);
  2741. Inc(regparse);
  2742. Break;
  2743. end;
  2744. // char '-' which (maybe) makes a range
  2745. if (regparse^ = '-') and ((regparse + 1) < fRegexEnd) and CanBeRange then
  2746. begin
  2747. Inc(regparse);
  2748. RangeEnd := regparse^;
  2749. if RangeEnd = EscChar then
  2750. begin
  2751. if _IsMetaChar((regparse + 1)^) then
  2752. begin
  2753. Error(reeMetaCharAfterMinusInRange);
  2754. Exit;
  2755. end;
  2756. Inc(regparse);
  2757. RangeEnd := UnQuoteChar(regparse);
  2758. end;
  2759. // special handling for Russian range a-YA, add 2 ranges: a-ya and A-YA
  2760. if fCompModifiers.R and
  2761. (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeHiHigh) then
  2762. begin
  2763. EmitRangePacked(RusRangeLoLow, RusRangeLoHigh);
  2764. EmitRangePacked(RusRangeHiLow, RusRangeHiHigh);
  2765. end
  2766. else
  2767. begin // standard r.e. handling
  2768. if RangeBeg > RangeEnd then
  2769. begin
  2770. Error(reeInvalidRange);
  2771. Exit;
  2772. end;
  2773. EmitRangePacked(RangeBeg, RangeEnd);
  2774. end;
  2775. Inc(regparse);
  2776. end
  2777. else
  2778. begin
  2779. if regparse^ = EscChar then
  2780. begin
  2781. Inc(regparse);
  2782. if regparse >= fRegexEnd then
  2783. begin
  2784. Error(reeParseAtomTrailingBackSlash);
  2785. Exit;
  2786. end;
  2787. if _IsMetaChar(regparse^) then
  2788. begin
  2789. AddrOfLen := nil;
  2790. CanBeRange := False;
  2791. EmitC(OpKind_MetaClass);
  2792. case regparse^ of
  2793. 'w':
  2794. EmitC(REChar(CheckerIndex_Word));
  2795. 'W':
  2796. EmitC(REChar(CheckerIndex_NotWord));
  2797. 's':
  2798. EmitC(REChar(CheckerIndex_Space));
  2799. 'S':
  2800. EmitC(REChar(CheckerIndex_NotSpace));
  2801. 'd':
  2802. EmitC(REChar(CheckerIndex_Digit));
  2803. 'D':
  2804. EmitC(REChar(CheckerIndex_NotDigit));
  2805. 'v':
  2806. EmitC(REChar(CheckerIndex_VertSep));
  2807. 'V':
  2808. EmitC(REChar(CheckerIndex_NotVertSep));
  2809. 'h':
  2810. EmitC(REChar(CheckerIndex_HorzSep));
  2811. 'H':
  2812. EmitC(REChar(CheckerIndex_NotHorzSep));
  2813. else
  2814. Error(reeBadOpcodeInCharClass);
  2815. end;
  2816. end
  2817. else
  2818. begin
  2819. TempChar := UnQuoteChar(regparse);
  2820. // False if '-' is last char in []
  2821. DashForRange :=
  2822. (regparse + 2 < fRegexEnd) and
  2823. ((regparse + 1)^ = '-') and
  2824. ((regparse + 2)^ <> ']');
  2825. EmitRangeChar(TempChar, DashForRange);
  2826. end;
  2827. end
  2828. else
  2829. begin
  2830. // False if '-' is last char in []
  2831. DashForRange :=
  2832. (regparse + 2 < fRegexEnd) and
  2833. ((regparse + 1)^ = '-') and
  2834. ((regparse + 2)^ <> ']');
  2835. EmitRangeChar(regparse^, DashForRange);
  2836. end;
  2837. Inc(regparse);
  2838. end;
  2839. end; { of while }
  2840. AddrOfLen := nil;
  2841. CanBeRange := False;
  2842. EmitC(OpKind_End);
  2843. if regparse^ <> ']' then
  2844. begin
  2845. Error(reeUnmatchedSqBrackets);
  2846. Exit;
  2847. end;
  2848. Inc(regparse);
  2849. flagp := flagp or flag_HasWidth or flag_Simple;
  2850. end;
  2851. '(':
  2852. begin
  2853. if regparse^ = '?' then
  2854. begin
  2855. // check for non-capturing group: (?:text)
  2856. if (regparse + 1)^ = ':' then
  2857. begin
  2858. Inc(regparse, 2);
  2859. ret := ParseReg(1, flags);
  2860. if ret = nil then
  2861. begin
  2862. Result := nil;
  2863. Exit;
  2864. end;
  2865. flagp := flagp or flags and (flag_HasWidth or flag_SpecStart);
  2866. end
  2867. else
  2868. // check for extended Perl syntax : (?..)
  2869. if (regparse + 1)^ = '#' then
  2870. begin // (?#comment)
  2871. Inc(regparse, 2); // find closing ')'
  2872. while (regparse < fRegexEnd) and (regparse^ <> ')') do
  2873. Inc(regparse);
  2874. if regparse^ <> ')' then
  2875. begin
  2876. Error(reeUnclosedComment);
  2877. Exit;
  2878. end;
  2879. Inc(regparse); // skip ')'
  2880. ret := EmitNode(OP_COMMENT); // comment
  2881. end
  2882. else
  2883. begin // modifiers ?
  2884. Inc(regparse); // skip '?'
  2885. SavedPtr := regparse;
  2886. while (regparse < fRegexEnd) and (regparse^ <> ')') do
  2887. Inc(regparse);
  2888. if (regparse^ <> ')') or
  2889. not ParseModifiers(SavedPtr, regparse - SavedPtr, fCompModifiers) then
  2890. begin
  2891. Error(reeUnrecognizedModifier);
  2892. Exit;
  2893. end;
  2894. Inc(regparse); // skip ')'
  2895. ret := EmitNode(OP_COMMENT); // comment
  2896. // Error (reeQPSBFollowsNothing);
  2897. // Exit;
  2898. end;
  2899. end
  2900. else
  2901. begin
  2902. // normal (capturing) group
  2903. if fSecondPass then
  2904. // must skip this block for one of passes, to not double groups count
  2905. if GrpCount < NSUBEXP - 1 then
  2906. begin
  2907. Inc(GrpCount);
  2908. GrpIndexes[GrpCount] := regnpar;
  2909. end;
  2910. ret := ParseReg(1, flags);
  2911. if ret = nil then
  2912. begin
  2913. Result := nil;
  2914. Exit;
  2915. end;
  2916. flagp := flagp or flags and (flag_HasWidth or flag_SpecStart);
  2917. end;
  2918. end;
  2919. '|', ')':
  2920. begin // Supposed to be caught earlier.
  2921. Error(reeInternalUrp);
  2922. Exit;
  2923. end;
  2924. '?', '+', '*':
  2925. begin
  2926. Error(reeQPSBFollowsNothing);
  2927. Exit;
  2928. end;
  2929. EscChar:
  2930. begin
  2931. if regparse >= fRegexEnd then
  2932. begin
  2933. Error(reeTrailingBackSlash);
  2934. Exit;
  2935. end;
  2936. case regparse^ of // r.e.extensions
  2937. 'b':
  2938. ret := EmitNode(OP_BOUND); // ###0.943
  2939. 'B':
  2940. ret := EmitNode(OP_NOTBOUND); // ###0.943
  2941. 'A':
  2942. ret := EmitNode(OP_BOL); // ###0.941
  2943. 'Z':
  2944. ret := EmitNode(OP_EOL); // ###0.941
  2945. 'd':
  2946. begin // r.e.extension - any digit ('0' .. '9')
  2947. ret := EmitNode(OP_ANYDIGIT);
  2948. flagp := flagp or flag_HasWidth or flag_Simple;
  2949. end;
  2950. 'D':
  2951. begin // r.e.extension - not digit ('0' .. '9')
  2952. ret := EmitNode(OP_NOTDIGIT);
  2953. flagp := flagp or flag_HasWidth or flag_Simple;
  2954. end;
  2955. 's':
  2956. begin // r.e.extension - any space char
  2957. ret := EmitNode(OP_ANYSPACE);
  2958. flagp := flagp or flag_HasWidth or flag_Simple;
  2959. end;
  2960. 'S':
  2961. begin // r.e.extension - not space char
  2962. ret := EmitNode(OP_NOTSPACE);
  2963. flagp := flagp or flag_HasWidth or flag_Simple;
  2964. end;
  2965. 'w':
  2966. begin // r.e.extension - any english char / digit / '_'
  2967. ret := EmitNode(OP_ANYLETTER);
  2968. flagp := flagp or flag_HasWidth or flag_Simple;
  2969. end;
  2970. 'W':
  2971. begin // r.e.extension - not english char / digit / '_'
  2972. ret := EmitNode(OP_NOTLETTER);
  2973. flagp := flagp or flag_HasWidth or flag_Simple;
  2974. end;
  2975. 'v':
  2976. begin
  2977. ret := EmitNode(OP_ANYVERTSEP);
  2978. flagp := flagp or flag_HasWidth or flag_Simple;
  2979. end;
  2980. 'V':
  2981. begin
  2982. ret := EmitNode(OP_NOTVERTSEP);
  2983. flagp := flagp or flag_HasWidth or flag_Simple;
  2984. end;
  2985. 'h':
  2986. begin
  2987. ret := EmitNode(OP_ANYHORZSEP);
  2988. flagp := flagp or flag_HasWidth or flag_Simple;
  2989. end;
  2990. 'H':
  2991. begin
  2992. ret := EmitNode(OP_NOTHORZSEP);
  2993. flagp := flagp or flag_HasWidth or flag_Simple;
  2994. end;
  2995. '1' .. '9':
  2996. begin // ###0.936
  2997. if fCompModifiers.I then
  2998. ret := EmitNode(OP_BSUBEXPCI)
  2999. else
  3000. ret := EmitNode(OP_BSUBEXP);
  3001. EmitC(REChar(Ord(regparse^) - Ord('0')));
  3002. flagp := flagp or flag_HasWidth or flag_Simple;
  3003. end;
  3004. else
  3005. EmitExactly(UnQuoteChar(regparse));
  3006. end; { of case }
  3007. Inc(regparse);
  3008. end;
  3009. else
  3010. begin
  3011. Dec(regparse);
  3012. if fCompModifiers.X and // check for eXtended syntax
  3013. ((regparse^ = '#') or IsIgnoredChar(regparse^)) then
  3014. begin // ###0.941 \x
  3015. if regparse^ = '#' then
  3016. begin // Skip eXtended comment
  3017. // find comment terminator (group of \n and/or \r)
  3018. while (regparse < fRegexEnd) and (regparse^ <> #$d) and
  3019. (regparse^ <> #$a) do
  3020. Inc(regparse);
  3021. while (regparse^ = #$d) or (regparse^ = #$a)
  3022. // skip comment terminator
  3023. do
  3024. Inc(regparse);
  3025. // attempt to support different type of line separators
  3026. end
  3027. else
  3028. begin // Skip the blanks!
  3029. while IsIgnoredChar(regparse^) do
  3030. Inc(regparse);
  3031. end;
  3032. ret := EmitNode(OP_COMMENT); // comment
  3033. end
  3034. else
  3035. begin
  3036. Len := FindSkippedMetaLen(regparse, fRegexEnd);
  3037. if Len <= 0 then
  3038. if regparse^ <> '{' then
  3039. begin
  3040. Error(reeRarseAtomInternalDisaster);
  3041. Exit;
  3042. end
  3043. else
  3044. Len := FindSkippedMetaLen(regparse + 1, fRegexEnd) + 1;
  3045. // bad {n,m} - compile as EXACTLY
  3046. EnderChar := (regparse + Len)^;
  3047. if (Len > 1) and ((EnderChar = '*') or (EnderChar = '+') or (EnderChar = '?') or (EnderChar = '{')) then
  3048. Dec(Len); // back off clear of ?+*{ operand.
  3049. flagp := flagp or flag_HasWidth;
  3050. if Len = 1 then
  3051. flagp := flagp or flag_Simple;
  3052. if fCompModifiers.I then
  3053. ret := EmitNode(OP_EXACTLYCI)
  3054. else
  3055. ret := EmitNode(OP_EXACTLY);
  3056. EmitInt(0);
  3057. while (Len > 0) and ((not fCompModifiers.X) or (regparse^ <> '#')) do
  3058. begin
  3059. if not fCompModifiers.X or not IsIgnoredChar(regparse^) then
  3060. begin
  3061. EmitC(regparse^);
  3062. if regcode <> @regdummy then
  3063. Inc(regExactlyLen^);
  3064. end;
  3065. Inc(regparse);
  3066. Dec(Len);
  3067. end;
  3068. end; { of if not comment }
  3069. end; { of case else }
  3070. end; { of case }
  3071. Result := ret;
  3072. end; { of function TRegExpr.ParseAtom
  3073. -------------------------------------------------------------- }
  3074. function TRegExpr.GetCompilerErrorPos: PtrInt;
  3075. begin
  3076. Result := 0;
  3077. if (regexpBegin = nil) or (regparse = nil) then
  3078. Exit; // not in compiling mode ?
  3079. Result := regparse - regexpBegin;
  3080. end; { of function TRegExpr.GetCompilerErrorPos
  3081. -------------------------------------------------------------- }
  3082. { ============================================================= }
  3083. { ===================== Matching section ====================== }
  3084. { ============================================================= }
  3085. function TRegExpr.regrepeat(p: PRegExprChar; AMax: integer): integer;
  3086. // repeatedly match something simple, report how many
  3087. var
  3088. scan: PRegExprChar;
  3089. opnd: PRegExprChar;
  3090. TheMax: PtrInt; // PtrInt, gets diff of 2 pointers
  3091. //NLen: integer;
  3092. InvChar: REChar; // ###0.931
  3093. GrpStart, GrpEnd: PRegExprChar; // ###0.936
  3094. ArrayIndex: integer;
  3095. begin
  3096. Result := 0;
  3097. scan := reginput;
  3098. opnd := p + REOpSz + RENextOffSz; // OPERAND
  3099. TheMax := fInputEnd - scan;
  3100. if TheMax > AMax then
  3101. TheMax := AMax;
  3102. case PREOp(p)^ of
  3103. OP_ANY:
  3104. begin
  3105. // note - OP_ANYML cannot be proceeded in regrepeat because can skip
  3106. // more than one char at once
  3107. Result := TheMax;
  3108. Inc(scan, Result);
  3109. end;
  3110. OP_EXACTLY:
  3111. begin // in opnd can be only ONE char !!!
  3112. {
  3113. // Alexey: commented because of https://github.com/andgineer/TRegExpr/issues/145
  3114. NLen := PLongInt(opnd)^;
  3115. if TheMax > NLen then
  3116. TheMax := NLen;
  3117. }
  3118. Inc(opnd, RENumberSz);
  3119. while (Result < TheMax) and (opnd^ = scan^) do
  3120. begin
  3121. Inc(Result);
  3122. Inc(scan);
  3123. end;
  3124. end;
  3125. OP_EXACTLYCI:
  3126. begin // in opnd can be only ONE char !!!
  3127. {
  3128. // Alexey: commented because of https://github.com/andgineer/TRegExpr/issues/145
  3129. NLen := PLongInt(opnd)^;
  3130. if TheMax > NLen then
  3131. TheMax := NLen;
  3132. }
  3133. Inc(opnd, RENumberSz);
  3134. while (Result < TheMax) and (opnd^ = scan^) do
  3135. begin // prevent unneeded InvertCase //###0.931
  3136. Inc(Result);
  3137. Inc(scan);
  3138. end;
  3139. if Result < TheMax then
  3140. begin // ###0.931
  3141. InvChar := InvertCase(opnd^); // store in register
  3142. while (Result < TheMax) and ((opnd^ = scan^) or (InvChar = scan^)) do
  3143. begin
  3144. Inc(Result);
  3145. Inc(scan);
  3146. end;
  3147. end;
  3148. end;
  3149. OP_BSUBEXP:
  3150. begin // ###0.936
  3151. ArrayIndex := GrpIndexes[Ord(opnd^)];
  3152. if ArrayIndex < 0 then
  3153. Exit;
  3154. GrpStart := startp[ArrayIndex];
  3155. if GrpStart = nil then
  3156. Exit;
  3157. GrpEnd := endp[ArrayIndex];
  3158. if GrpEnd = nil then
  3159. Exit;
  3160. repeat
  3161. opnd := GrpStart;
  3162. while opnd < GrpEnd do
  3163. begin
  3164. if (scan >= fInputEnd) or (scan^ <> opnd^) then
  3165. Exit;
  3166. Inc(scan);
  3167. Inc(opnd);
  3168. end;
  3169. Inc(Result);
  3170. reginput := scan;
  3171. until Result >= AMax;
  3172. end;
  3173. OP_BSUBEXPCI:
  3174. begin // ###0.936
  3175. ArrayIndex := GrpIndexes[Ord(opnd^)];
  3176. if ArrayIndex < 0 then
  3177. Exit;
  3178. GrpStart := startp[ArrayIndex];
  3179. if GrpStart = nil then
  3180. Exit;
  3181. GrpEnd := endp[ArrayIndex];
  3182. if GrpEnd = nil then
  3183. Exit;
  3184. repeat
  3185. opnd := GrpStart;
  3186. while opnd < GrpEnd do
  3187. begin
  3188. if (scan >= fInputEnd) or
  3189. ((scan^ <> opnd^) and (scan^ <> InvertCase(opnd^))) then
  3190. Exit;
  3191. Inc(scan);
  3192. Inc(opnd);
  3193. end;
  3194. Inc(Result);
  3195. reginput := scan;
  3196. until Result >= AMax;
  3197. end;
  3198. OP_ANYDIGIT:
  3199. while (Result < TheMax) and IsDigitChar(scan^) do
  3200. begin
  3201. Inc(Result);
  3202. Inc(scan);
  3203. end;
  3204. OP_NOTDIGIT:
  3205. while (Result < TheMax) and not IsDigitChar(scan^) do
  3206. begin
  3207. Inc(Result);
  3208. Inc(scan);
  3209. end;
  3210. OP_ANYLETTER:
  3211. while (Result < TheMax) and IsWordChar(scan^) do // ###0.940
  3212. begin
  3213. Inc(Result);
  3214. Inc(scan);
  3215. end;
  3216. OP_NOTLETTER:
  3217. while (Result < TheMax) and not IsWordChar(scan^) do // ###0.940
  3218. begin
  3219. Inc(Result);
  3220. Inc(scan);
  3221. end;
  3222. OP_ANYSPACE:
  3223. while (Result < TheMax) and IsSpaceChar(scan^) do
  3224. begin
  3225. Inc(Result);
  3226. Inc(scan);
  3227. end;
  3228. OP_NOTSPACE:
  3229. while (Result < TheMax) and not IsSpaceChar(scan^) do
  3230. begin
  3231. Inc(Result);
  3232. Inc(scan);
  3233. end;
  3234. OP_ANYVERTSEP:
  3235. while (Result < TheMax) and IsLineSeparator(scan^) do
  3236. begin
  3237. Inc(Result);
  3238. Inc(scan);
  3239. end;
  3240. OP_NOTVERTSEP:
  3241. while (Result < TheMax) and not IsLineSeparator(scan^) do
  3242. begin
  3243. Inc(Result);
  3244. Inc(scan);
  3245. end;
  3246. OP_ANYHORZSEP:
  3247. while (Result < TheMax) and IsHorzSeparator(scan^) do
  3248. begin
  3249. Inc(Result);
  3250. Inc(scan);
  3251. end;
  3252. OP_NOTHORZSEP:
  3253. while (Result < TheMax) and not IsHorzSeparator(scan^) do
  3254. begin
  3255. Inc(Result);
  3256. Inc(scan);
  3257. end;
  3258. OP_ANYOF:
  3259. while (Result < TheMax) and FindInCharClass(opnd, scan^, False) do
  3260. begin
  3261. Inc(Result);
  3262. Inc(scan);
  3263. end;
  3264. OP_ANYBUT:
  3265. while (Result < TheMax) and not FindInCharClass(opnd, scan^, False) do
  3266. begin
  3267. Inc(Result);
  3268. Inc(scan);
  3269. end;
  3270. OP_ANYOFCI:
  3271. while (Result < TheMax) and FindInCharClass(opnd, scan^, True) do
  3272. begin
  3273. Inc(Result);
  3274. Inc(scan);
  3275. end;
  3276. OP_ANYBUTCI:
  3277. while (Result < TheMax) and not FindInCharClass(opnd, scan^, True) do
  3278. begin
  3279. Inc(Result);
  3280. Inc(scan);
  3281. end;
  3282. else
  3283. begin // Oh dear. Called inappropriately.
  3284. Result := 0; // Best compromise.
  3285. Error(reeRegRepeatCalledInappropriately);
  3286. Exit;
  3287. end;
  3288. end; { of case }
  3289. reginput := scan;
  3290. end; { of function TRegExpr.regrepeat
  3291. -------------------------------------------------------------- }
  3292. function TRegExpr.regnext(p: PRegExprChar): PRegExprChar;
  3293. // dig the "next" pointer out of a node
  3294. var
  3295. offset: TRENextOff;
  3296. begin
  3297. if p = @regdummy then
  3298. begin
  3299. Result := nil;
  3300. Exit;
  3301. end;
  3302. offset := PRENextOff(AlignToPtr(p + REOpSz))^; // ###0.933 inlined NEXT
  3303. if offset = 0 then
  3304. Result := nil
  3305. else
  3306. Result := p + offset;
  3307. end; { of function TRegExpr.regnext
  3308. -------------------------------------------------------------- }
  3309. function TRegExpr.MatchPrim(prog: PRegExprChar): boolean;
  3310. // recursively matching routine
  3311. // Conceptually the strategy is simple: check to see whether the current
  3312. // node matches, call self recursively to see whether the rest matches,
  3313. // and then act accordingly. In practice we make some effort to avoid
  3314. // recursion, in particular by going through "ordinary" nodes (that don't
  3315. // need to know whether the rest of the match failed) by a loop instead of
  3316. // by recursion.
  3317. var
  3318. scan: PRegExprChar; // Current node.
  3319. next: PRegExprChar; // Next node.
  3320. Len: PtrInt;
  3321. opnd: PRegExprChar;
  3322. no: integer;
  3323. save: PRegExprChar;
  3324. nextch: REChar;
  3325. BracesMin, Bracesmax: integer;
  3326. // we use integer instead of TREBracesArg for better support */+
  3327. {$IFDEF ComplexBraces}
  3328. SavedLoopStack: TRegExprLoopStack; // :(( very bad for recursion
  3329. SavedLoopStackIdx: integer; // ###0.925
  3330. {$ENDIF}
  3331. bound1, bound2: boolean;
  3332. begin
  3333. Result := False;
  3334. {$IFDEF ComplexBraces}
  3335. SavedLoopStack:=Default(TRegExprLoopStack);
  3336. SavedLoopStackIdx:=0;
  3337. {$ENDIF}
  3338. scan := prog;
  3339. while scan <> nil do
  3340. begin
  3341. Len := PRENextOff(AlignToPtr(scan + 1))^; // ###0.932 inlined regnext
  3342. if Len = 0 then
  3343. next := nil
  3344. else
  3345. next := scan + Len;
  3346. case scan^ of
  3347. OP_NOTBOUND,
  3348. OP_BOUND:
  3349. begin
  3350. bound1 := (reginput = fInputStart) or not IsWordChar((reginput - 1)^);
  3351. bound2 := (reginput = fInputEnd) or not IsWordChar(reginput^);
  3352. if (scan^ = OP_BOUND) xor (bound1 <> bound2) then
  3353. Exit;
  3354. end;
  3355. OP_BOL:
  3356. begin
  3357. if reginput <> fInputStart then
  3358. Exit;
  3359. end;
  3360. OP_EOL:
  3361. begin
  3362. if reginput < fInputEnd then
  3363. Exit;
  3364. end;
  3365. OP_BOLML:
  3366. if reginput > fInputStart then
  3367. begin
  3368. nextch := (reginput - 1)^;
  3369. if (nextch <> fLinePairedSeparatorTail) or
  3370. ((reginput - 1) <= fInputStart) or
  3371. ((reginput - 2)^ <> fLinePairedSeparatorHead) then
  3372. begin
  3373. if (nextch = fLinePairedSeparatorHead) and
  3374. (reginput^ = fLinePairedSeparatorTail) then
  3375. Exit; // don't stop between paired separator
  3376. if not IsCustomLineSeparator(nextch) then
  3377. Exit;
  3378. end;
  3379. end;
  3380. OP_EOLML:
  3381. if reginput < fInputEnd then
  3382. begin
  3383. nextch := reginput^;
  3384. if (nextch <> fLinePairedSeparatorHead) or
  3385. ((reginput + 1)^ <> fLinePairedSeparatorTail) then
  3386. begin
  3387. if (nextch = fLinePairedSeparatorTail) and (reginput > fInputStart)
  3388. and ((reginput - 1)^ = fLinePairedSeparatorHead) then
  3389. Exit; // don't stop between paired separator
  3390. if not IsCustomLineSeparator(nextch) then
  3391. Exit;
  3392. end;
  3393. end;
  3394. OP_ANY:
  3395. begin
  3396. if reginput = fInputEnd then
  3397. Exit;
  3398. Inc(reginput);
  3399. end;
  3400. OP_ANYML:
  3401. begin // ###0.941
  3402. if (reginput = fInputEnd) or
  3403. ((reginput^ = fLinePairedSeparatorHead) and
  3404. ((reginput + 1)^ = fLinePairedSeparatorTail)) or
  3405. IsCustomLineSeparator(reginput^)
  3406. then
  3407. Exit;
  3408. Inc(reginput);
  3409. end;
  3410. OP_ANYDIGIT:
  3411. begin
  3412. if (reginput = fInputEnd) or not IsDigitChar(reginput^) then
  3413. Exit;
  3414. Inc(reginput);
  3415. end;
  3416. OP_NOTDIGIT:
  3417. begin
  3418. if (reginput = fInputEnd) or IsDigitChar(reginput^) then
  3419. Exit;
  3420. Inc(reginput);
  3421. end;
  3422. OP_ANYLETTER:
  3423. begin
  3424. if (reginput = fInputEnd) or not IsWordChar(reginput^) // ###0.943
  3425. then
  3426. Exit;
  3427. Inc(reginput);
  3428. end;
  3429. OP_NOTLETTER:
  3430. begin
  3431. if (reginput = fInputEnd) or IsWordChar(reginput^) // ###0.943
  3432. then
  3433. Exit;
  3434. Inc(reginput);
  3435. end;
  3436. OP_ANYSPACE:
  3437. begin
  3438. if (reginput = fInputEnd) or not IsSpaceChar(reginput^) // ###0.943
  3439. then
  3440. Exit;
  3441. Inc(reginput);
  3442. end;
  3443. OP_NOTSPACE:
  3444. begin
  3445. if (reginput = fInputEnd) or IsSpaceChar(reginput^) // ###0.943
  3446. then
  3447. Exit;
  3448. Inc(reginput);
  3449. end;
  3450. OP_ANYVERTSEP:
  3451. begin
  3452. if (reginput = fInputEnd) or not IsLineSeparator(reginput^) then
  3453. Exit;
  3454. Inc(reginput);
  3455. end;
  3456. OP_NOTVERTSEP:
  3457. begin
  3458. if (reginput = fInputEnd) or IsLineSeparator(reginput^) then
  3459. Exit;
  3460. Inc(reginput);
  3461. end;
  3462. OP_ANYHORZSEP:
  3463. begin
  3464. if (reginput = fInputEnd) or not IsHorzSeparator(reginput^) then
  3465. Exit;
  3466. Inc(reginput);
  3467. end;
  3468. OP_NOTHORZSEP:
  3469. begin
  3470. if (reginput = fInputEnd) or IsHorzSeparator(reginput^) then
  3471. Exit;
  3472. Inc(reginput);
  3473. end;
  3474. OP_EXACTLYCI:
  3475. begin
  3476. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  3477. Len := PLongInt(opnd)^;
  3478. Inc(opnd, RENumberSz);
  3479. // Inline the first character, for speed.
  3480. if (opnd^ <> reginput^) and (InvertCase(opnd^) <> reginput^) then
  3481. Exit;
  3482. // ###0.929 begin
  3483. no := Len;
  3484. save := reginput;
  3485. while no > 1 do
  3486. begin
  3487. Inc(save);
  3488. Inc(opnd);
  3489. if (opnd^ <> save^) and (InvertCase(opnd^) <> save^) then
  3490. Exit;
  3491. Dec(no);
  3492. end;
  3493. // ###0.929 end
  3494. Inc(reginput, Len);
  3495. end;
  3496. OP_EXACTLY:
  3497. begin
  3498. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  3499. Len := PLongInt(opnd)^;
  3500. Inc(opnd, RENumberSz);
  3501. // Inline the first character, for speed.
  3502. if opnd^ <> reginput^ then
  3503. Exit;
  3504. // ###0.929 begin
  3505. no := Len;
  3506. save := reginput;
  3507. while no > 1 do
  3508. begin
  3509. Inc(save);
  3510. Inc(opnd);
  3511. if opnd^ <> save^ then
  3512. Exit;
  3513. Dec(no);
  3514. end;
  3515. // ###0.929 end
  3516. Inc(reginput, Len);
  3517. end;
  3518. OP_BSUBEXP:
  3519. begin // ###0.936
  3520. no := Ord((scan + REOpSz + RENextOffSz)^);
  3521. no := GrpIndexes[no];
  3522. if no < 0 then
  3523. Exit;
  3524. if startp[no] = nil then
  3525. Exit;
  3526. if endp[no] = nil then
  3527. Exit;
  3528. save := reginput;
  3529. opnd := startp[no];
  3530. while opnd < endp[no] do
  3531. begin
  3532. if (save >= fInputEnd) or (save^ <> opnd^) then
  3533. Exit;
  3534. Inc(save);
  3535. Inc(opnd);
  3536. end;
  3537. reginput := save;
  3538. end;
  3539. OP_BSUBEXPCI:
  3540. begin // ###0.936
  3541. no := Ord((scan + REOpSz + RENextOffSz)^);
  3542. no := GrpIndexes[no];
  3543. if no < 0 then
  3544. Exit;
  3545. if startp[no] = nil then
  3546. Exit;
  3547. if endp[no] = nil then
  3548. Exit;
  3549. save := reginput;
  3550. opnd := startp[no];
  3551. while opnd < endp[no] do
  3552. begin
  3553. if (save >= fInputEnd) or
  3554. ((save^ <> opnd^) and (save^ <> InvertCase(opnd^))) then
  3555. Exit;
  3556. Inc(save);
  3557. Inc(opnd);
  3558. end;
  3559. reginput := save;
  3560. end;
  3561. OP_ANYOF:
  3562. begin
  3563. if (reginput = fInputEnd) or
  3564. not FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, False) then
  3565. Exit;
  3566. Inc(reginput);
  3567. end;
  3568. OP_ANYBUT:
  3569. begin
  3570. if (reginput = fInputEnd) or
  3571. FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, False) then
  3572. Exit;
  3573. Inc(reginput);
  3574. end;
  3575. OP_ANYOFCI:
  3576. begin
  3577. if (reginput = fInputEnd) or
  3578. not FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, True) then
  3579. Exit;
  3580. Inc(reginput);
  3581. end;
  3582. OP_ANYBUTCI:
  3583. begin
  3584. if (reginput = fInputEnd) or
  3585. FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, True) then
  3586. Exit;
  3587. Inc(reginput);
  3588. end;
  3589. OP_NOTHING:
  3590. ;
  3591. OP_COMMENT:
  3592. ;
  3593. OP_BACK:
  3594. ;
  3595. Succ(OP_OPEN) .. TREOp(Ord(OP_OPEN) + NSUBEXP - 1):
  3596. begin // ###0.929
  3597. no := Ord(scan^) - Ord(OP_OPEN);
  3598. // save := reginput;
  3599. save := startp[no]; // ###0.936
  3600. startp[no] := reginput; // ###0.936
  3601. Result := MatchPrim(next);
  3602. if not Result // ###0.936
  3603. then
  3604. startp[no] := save;
  3605. // if Result and (startp [no] = nil)
  3606. // then startp [no] := save;
  3607. // Don't set startp if some later invocation of the same
  3608. // parentheses already has.
  3609. Exit;
  3610. end;
  3611. Succ(OP_CLOSE) .. TREOp(Ord(OP_CLOSE) + NSUBEXP - 1):
  3612. begin // ###0.929
  3613. no := Ord(scan^) - Ord(OP_CLOSE);
  3614. // save := reginput;
  3615. save := endp[no]; // ###0.936
  3616. endp[no] := reginput; // ###0.936
  3617. Result := MatchPrim(next);
  3618. if not Result // ###0.936
  3619. then
  3620. endp[no] := save;
  3621. // if Result and (endp [no] = nil)
  3622. // then endp [no] := save;
  3623. // Don't set endp if some later invocation of the same
  3624. // parentheses already has.
  3625. Exit;
  3626. end;
  3627. OP_BRANCH:
  3628. begin
  3629. if (next^ <> OP_BRANCH) // No choice.
  3630. then
  3631. next := scan + REOpSz + RENextOffSz // Avoid recursion
  3632. else
  3633. begin
  3634. repeat
  3635. save := reginput;
  3636. Result := MatchPrim(scan + REOpSz + RENextOffSz);
  3637. if Result then
  3638. Exit;
  3639. reginput := save;
  3640. scan := regnext(scan);
  3641. until (scan = nil) or (scan^ <> OP_BRANCH);
  3642. Exit;
  3643. end;
  3644. end;
  3645. {$IFDEF ComplexBraces}
  3646. OP_LOOPENTRY:
  3647. begin // ###0.925
  3648. no := LoopStackIdx;
  3649. Inc(LoopStackIdx);
  3650. if LoopStackIdx > LoopStackMax then
  3651. begin
  3652. Error(reeLoopStackExceeded);
  3653. Exit;
  3654. end;
  3655. save := reginput;
  3656. LoopStack[LoopStackIdx] := 0; // init loop counter
  3657. Result := MatchPrim(next); // execute loop
  3658. LoopStackIdx := no; // cleanup
  3659. if Result then
  3660. Exit;
  3661. reginput := save;
  3662. Exit;
  3663. end;
  3664. OP_LOOP, OP_LOOPNG:
  3665. begin // ###0.940
  3666. if LoopStackIdx <= 0 then
  3667. begin
  3668. Error(reeLoopWithoutEntry);
  3669. Exit;
  3670. end;
  3671. opnd := scan + PRENextOff(AlignToPtr(scan + REOpSz + RENextOffSz + 2 * REBracesArgSz))^;
  3672. BracesMin := PREBracesArg(AlignToInt(scan + REOpSz + RENextOffSz))^;
  3673. Bracesmax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  3674. save := reginput;
  3675. if LoopStack[LoopStackIdx] >= BracesMin then
  3676. begin // Min alredy matched - we can work
  3677. if scan^ = OP_LOOP then
  3678. begin
  3679. // greedy way - first try to max deep of greed ;)
  3680. if LoopStack[LoopStackIdx] < Bracesmax then
  3681. begin
  3682. Inc(LoopStack[LoopStackIdx]);
  3683. no := LoopStackIdx;
  3684. Result := MatchPrim(opnd);
  3685. LoopStackIdx := no;
  3686. if Result then
  3687. Exit;
  3688. reginput := save;
  3689. end;
  3690. Dec(LoopStackIdx); // Fail. May be we are too greedy? ;)
  3691. Result := MatchPrim(next);
  3692. if not Result then
  3693. reginput := save;
  3694. Exit;
  3695. end
  3696. else
  3697. begin
  3698. // non-greedy - try just now
  3699. Result := MatchPrim(next);
  3700. if Result then
  3701. Exit
  3702. else
  3703. reginput := save; // failed - move next and try again
  3704. if LoopStack[LoopStackIdx] < Bracesmax then
  3705. begin
  3706. Inc(LoopStack[LoopStackIdx]);
  3707. no := LoopStackIdx;
  3708. Result := MatchPrim(opnd);
  3709. LoopStackIdx := no;
  3710. if Result then
  3711. Exit;
  3712. reginput := save;
  3713. end;
  3714. Dec(LoopStackIdx); // Failed - back up
  3715. Exit;
  3716. end
  3717. end
  3718. else
  3719. begin // first match a min_cnt times
  3720. Inc(LoopStack[LoopStackIdx]);
  3721. no := LoopStackIdx;
  3722. Result := MatchPrim(opnd);
  3723. LoopStackIdx := no;
  3724. if Result then
  3725. Exit;
  3726. Dec(LoopStack[LoopStackIdx]);
  3727. reginput := save;
  3728. Exit;
  3729. end;
  3730. end;
  3731. {$ENDIF}
  3732. OP_STAR, OP_PLUS, OP_BRACES, OP_STARNG, OP_PLUSNG, OP_BRACESNG:
  3733. begin
  3734. // Lookahead to avoid useless match attempts when we know
  3735. // what character comes next.
  3736. nextch := #0;
  3737. if next^ = OP_EXACTLY then
  3738. nextch := (next + REOpSz + RENextOffSz + RENumberSz)^;
  3739. Bracesmax := MaxInt; // infinite loop for * and + //###0.92
  3740. if (scan^ = OP_STAR) or (scan^ = OP_STARNG) then
  3741. BracesMin := 0 // star
  3742. else if (scan^ = OP_PLUS) or (scan^ = OP_PLUSNG) then
  3743. BracesMin := 1 // plus
  3744. else
  3745. begin // braces
  3746. BracesMin := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  3747. Bracesmax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  3748. end;
  3749. save := reginput;
  3750. opnd := scan + REOpSz + RENextOffSz;
  3751. if (scan^ = OP_BRACES) or (scan^ = OP_BRACESNG) then
  3752. Inc(opnd, 2 * REBracesArgSz);
  3753. if (scan^ = OP_PLUSNG) or (scan^ = OP_STARNG) or (scan^ = OP_BRACESNG) then
  3754. begin
  3755. // non-greedy mode
  3756. Bracesmax := regrepeat(opnd, Bracesmax);
  3757. // don't repeat more than BracesMax
  3758. // Now we know real Max limit to move forward (for recursion 'back up')
  3759. // In some cases it can be faster to check only Min positions first,
  3760. // but after that we have to check every position separtely instead
  3761. // of fast scannig in loop.
  3762. no := BracesMin;
  3763. while no <= Bracesmax do
  3764. begin
  3765. reginput := save + no;
  3766. // If it could work, try it.
  3767. if (nextch = #0) or (reginput^ = nextch) then
  3768. begin
  3769. {$IFDEF ComplexBraces}
  3770. System.Move(LoopStack, SavedLoopStack, SizeOf(LoopStack));
  3771. // ###0.925
  3772. SavedLoopStackIdx := LoopStackIdx;
  3773. {$ENDIF}
  3774. if MatchPrim(next) then
  3775. begin
  3776. Result := True;
  3777. Exit;
  3778. end;
  3779. {$IFDEF ComplexBraces}
  3780. System.Move(SavedLoopStack, LoopStack, SizeOf(LoopStack));
  3781. LoopStackIdx := SavedLoopStackIdx;
  3782. {$ENDIF}
  3783. end;
  3784. Inc(no); // Couldn't or didn't - move forward.
  3785. end; { of while }
  3786. Exit;
  3787. end
  3788. else
  3789. begin // greedy mode
  3790. no := regrepeat(opnd, Bracesmax); // don't repeat more than max_cnt
  3791. while no >= BracesMin do
  3792. begin
  3793. // If it could work, try it.
  3794. if (nextch = #0) or (reginput^ = nextch) then
  3795. begin
  3796. {$IFDEF ComplexBraces}
  3797. System.Move(LoopStack, SavedLoopStack, SizeOf(LoopStack));
  3798. // ###0.925
  3799. SavedLoopStackIdx := LoopStackIdx;
  3800. {$ENDIF}
  3801. if MatchPrim(next) then
  3802. begin
  3803. Result := True;
  3804. Exit;
  3805. end;
  3806. {$IFDEF ComplexBraces}
  3807. System.Move(SavedLoopStack, LoopStack, SizeOf(LoopStack));
  3808. LoopStackIdx := SavedLoopStackIdx;
  3809. {$ENDIF}
  3810. end;
  3811. Dec(no); // Couldn't or didn't - back up.
  3812. reginput := save + no;
  3813. end; { of while }
  3814. Exit;
  3815. end;
  3816. end;
  3817. OP_EEND:
  3818. begin
  3819. Result := True; // Success!
  3820. Exit;
  3821. end;
  3822. else
  3823. begin
  3824. Error(reeMatchPrimMemoryCorruption);
  3825. Exit;
  3826. end;
  3827. end; { of case scan^ }
  3828. scan := next;
  3829. end; { of while scan <> nil }
  3830. // We get here only if there's trouble -- normally "case EEND" is the
  3831. // terminating point.
  3832. Error(reeMatchPrimCorruptedPointers);
  3833. end; { of function TRegExpr.MatchPrim
  3834. -------------------------------------------------------------- }
  3835. function TRegExpr.Exec(const AInputString: RegExprString): boolean;
  3836. begin
  3837. InputString := AInputString;
  3838. Result := ExecPrim(1, False, False, False);
  3839. end; { of function TRegExpr.Exec
  3840. -------------------------------------------------------------- }
  3841. function TRegExpr.Exec: boolean;
  3842. var
  3843. SlowChecks: boolean;
  3844. begin
  3845. SlowChecks := Length(fInputString) < fSlowChecksSizeMax;
  3846. Result := ExecPrim(1, False, SlowChecks, False);
  3847. end; { of function TRegExpr.Exec
  3848. -------------------------------------------------------------- }
  3849. function TRegExpr.Exec(AOffset: integer): boolean;
  3850. begin
  3851. Result := ExecPrim(AOffset, False, False, False);
  3852. end; { of function TRegExpr.Exec
  3853. -------------------------------------------------------------- }
  3854. function TRegExpr.ExecPos(AOffset: integer = 1): boolean;
  3855. begin
  3856. Result := ExecPrim(AOffset, False, False, False);
  3857. end; { of function TRegExpr.ExecPos
  3858. -------------------------------------------------------------- }
  3859. function TRegExpr.ExecPos(AOffset: integer; ATryOnce: boolean): boolean;
  3860. begin
  3861. Result := ExecPrim(AOffset, ATryOnce, False, False);
  3862. end;
  3863. function TRegExpr.ExecPos(AOffset: integer; ATryOnce, aBackward: boolean): boolean;
  3864. begin
  3865. Result := ExecPrim(AOffset, ATryOnce, False, ABackward);
  3866. end;
  3867. function TRegExpr.MatchAtOnePos(APos: PRegExprChar): boolean;
  3868. begin
  3869. reginput := APos;
  3870. Result := MatchPrim(programm + REOpSz);
  3871. if Result then
  3872. begin
  3873. startp[0] := APos;
  3874. endp[0] := reginput;
  3875. end;
  3876. end;
  3877. procedure TRegExpr.ClearMatches;
  3878. begin
  3879. FillChar(startp, SizeOf(startp), 0);
  3880. FillChar(endp, SizeOf(endp), 0);
  3881. end;
  3882. procedure TRegExpr.ClearInternalIndexes;
  3883. var
  3884. i: integer;
  3885. begin
  3886. FillChar(startp, SizeOf(startp), 0);
  3887. FillChar(endp, SizeOf(endp), 0);
  3888. for i := 0 to NSUBEXP - 1 do
  3889. GrpIndexes[i] := -1;
  3890. GrpIndexes[0] := 0;
  3891. GrpCount := 0;
  3892. end;
  3893. function TRegExpr.ExecPrim(AOffset: integer;
  3894. ATryOnce, ASlowChecks, ABackward: boolean): boolean;
  3895. var
  3896. Ptr: PRegExprChar;
  3897. begin
  3898. Result := False;
  3899. // Ensure that Match cleared either if optimization tricks or some error
  3900. // will lead to leaving ExecPrim without actual search. That is
  3901. // important for ExecNext logic and so on.
  3902. ClearMatches;
  3903. // Don't check IsProgrammOk here! it causes big slowdown in test_benchmark!
  3904. if programm = nil then
  3905. begin
  3906. Compile;
  3907. if programm = nil then
  3908. Exit;
  3909. end;
  3910. // Check InputString presence
  3911. if fInputString = '' then
  3912. begin
  3913. if EmptyInputRaisesError then
  3914. Error(reeNoInputStringSpecified);
  3915. Exit;
  3916. end;
  3917. // Check that the start position is not negative
  3918. if AOffset < 1 then
  3919. begin
  3920. Error(reeOffsetMustBePositive);
  3921. Exit;
  3922. end;
  3923. // Check that the start position is not longer than the line
  3924. // If so then exit with nothing found
  3925. if AOffset > (Length(fInputString) + 1) // for matching empty string after last char.
  3926. then
  3927. Exit;
  3928. Ptr := fInputStart + AOffset - 1;
  3929. // If there is a "must appear" string, look for it.
  3930. if ASlowChecks then
  3931. if regmustString <> '' then
  3932. if Pos(regmustString, fInputString) = 0 then Exit;
  3933. {$IFDEF ComplexBraces}
  3934. // no loops started
  3935. LoopStackIdx := 0; // ###0.925
  3936. {$ENDIF}
  3937. // ATryOnce or anchored match (it needs to be tried only once).
  3938. if ATryOnce or (reganchored <> #0) then
  3939. begin
  3940. {$IFDEF UseFirstCharSet}
  3941. {$IFDEF UniCode}
  3942. if Ord(Ptr^) <= $FF then
  3943. {$ENDIF}
  3944. if not FirstCharArray[byte(Ptr^)] then
  3945. Exit;
  3946. {$ENDIF}
  3947. Result := MatchAtOnePos(Ptr);
  3948. Exit;
  3949. end;
  3950. // Messy cases: unanchored match.
  3951. if ABackward then
  3952. Inc(Ptr, 2)
  3953. else
  3954. Dec(Ptr);
  3955. repeat
  3956. if ABackward then
  3957. begin
  3958. Dec(Ptr);
  3959. if Ptr < fInputStart then
  3960. Exit;
  3961. end
  3962. else
  3963. begin
  3964. Inc(Ptr);
  3965. if Ptr > fInputEnd then
  3966. Exit;
  3967. end;
  3968. {$IFDEF UseFirstCharSet}
  3969. {$IFDEF UniCode}
  3970. if Ord(Ptr^) <= $FF then
  3971. {$ENDIF}
  3972. if not FirstCharArray[byte(Ptr^)] then
  3973. Continue;
  3974. {$ENDIF}
  3975. Result := MatchAtOnePos(Ptr);
  3976. // Exit on a match or after testing the end-of-string
  3977. if Result then
  3978. Exit;
  3979. until False;
  3980. end; { of function TRegExpr.ExecPrim
  3981. -------------------------------------------------------------- }
  3982. function TRegExpr.ExecNext: boolean;
  3983. begin
  3984. Result:=ExecNext(False);
  3985. end;
  3986. function TRegExpr.ExecNext(ABackward: boolean): boolean;
  3987. var
  3988. PtrBegin, PtrEnd: PRegExprChar;
  3989. Offset: PtrInt;
  3990. begin
  3991. PtrBegin := startp[0];
  3992. PtrEnd := endp[0];
  3993. if (PtrBegin = nil) or (PtrEnd = nil) then
  3994. begin
  3995. Error(reeExecNextWithoutExec);
  3996. Result := False;
  3997. Exit;
  3998. end;
  3999. Offset := PtrEnd - fInputStart + 1;
  4000. // prevent infinite looping if empty string matches r.e.
  4001. if PtrBegin = PtrEnd then
  4002. Inc(Offset);
  4003. Result := ExecPrim(Offset, False, False, ABackward);
  4004. end; { of function TRegExpr.ExecNext
  4005. -------------------------------------------------------------- }
  4006. procedure TRegExpr.SetInputString(const AInputString: RegExprString);
  4007. begin
  4008. ClearMatches;
  4009. fInputString := AInputString;
  4010. UniqueString(fInputString);
  4011. fInputStart := PRegExprChar(fInputString);
  4012. fInputEnd := fInputStart + Length(fInputString);
  4013. end; { of procedure TRegExpr.SetInputString
  4014. -------------------------------------------------------------- }
  4015. procedure TRegExpr.SetLineSeparators(const AStr: RegExprString);
  4016. begin
  4017. if AStr <> fLineSeparators then
  4018. begin
  4019. fLineSeparators := AStr;
  4020. InitLineSepArray;
  4021. InvalidateProgramm;
  4022. end;
  4023. end; { of procedure TRegExpr.SetLineSeparators
  4024. -------------------------------------------------------------- }
  4025. procedure TRegExpr.SetLinePairedSeparator(const AStr: RegExprString);
  4026. begin
  4027. if Length(AStr) = 2 then
  4028. begin
  4029. if AStr[1] = AStr[2] then
  4030. begin
  4031. // it's impossible for our 'one-point' checking to support
  4032. // two chars separator for identical chars
  4033. Error(reeBadLinePairedSeparator);
  4034. Exit;
  4035. end;
  4036. if not fLinePairedSeparatorAssigned or (AStr[1] <> fLinePairedSeparatorHead)
  4037. or (AStr[2] <> fLinePairedSeparatorTail) then
  4038. begin
  4039. fLinePairedSeparatorAssigned := True;
  4040. fLinePairedSeparatorHead := AStr[1];
  4041. fLinePairedSeparatorTail := AStr[2];
  4042. InvalidateProgramm;
  4043. end;
  4044. end
  4045. else if Length(AStr) = 0 then
  4046. begin
  4047. if fLinePairedSeparatorAssigned then
  4048. begin
  4049. fLinePairedSeparatorAssigned := False;
  4050. InvalidateProgramm;
  4051. end;
  4052. end
  4053. else
  4054. Error(reeBadLinePairedSeparator);
  4055. end; { of procedure TRegExpr.SetLinePairedSeparator
  4056. -------------------------------------------------------------- }
  4057. function TRegExpr.GetLinePairedSeparator: RegExprString;
  4058. begin
  4059. if fLinePairedSeparatorAssigned then
  4060. begin
  4061. {$IFDEF UniCode}
  4062. // Here is some UniCode 'magic'
  4063. // If You do know better decision to concatenate
  4064. // two WideChars, please, let me know!
  4065. Result := fLinePairedSeparatorHead; // ###0.947
  4066. Result := Result + fLinePairedSeparatorTail;
  4067. {$ELSE}
  4068. Result := fLinePairedSeparatorHead + fLinePairedSeparatorTail;
  4069. {$ENDIF}
  4070. end
  4071. else
  4072. Result := '';
  4073. end; { of function TRegExpr.GetLinePairedSeparator
  4074. -------------------------------------------------------------- }
  4075. function TRegExpr.Substitute(const ATemplate: RegExprString): RegExprString;
  4076. // perform substitutions after a regexp match
  4077. var
  4078. TemplateBeg, TemplateEnd: PRegExprChar;
  4079. function ParseVarName(var APtr: PRegExprChar): integer;
  4080. // extract name of variable (digits, may be enclosed with
  4081. // curly braces) from APtr^, uses TemplateEnd !!!
  4082. var
  4083. p: PRegExprChar;
  4084. Delimited: boolean;
  4085. begin
  4086. Result := 0;
  4087. p := APtr;
  4088. Delimited := (p < TemplateEnd) and (p^ = '{');
  4089. if Delimited then
  4090. Inc(p); // skip left curly brace
  4091. if (p < TemplateEnd) and (p^ = '&') then
  4092. Inc(p) // this is '$&' or '${&}'
  4093. else
  4094. while (p < TemplateEnd) and IsDigitChar(p^) do
  4095. begin
  4096. Result := Result * 10 + (Ord(p^) - Ord('0')); // ###0.939
  4097. Inc(p);
  4098. end;
  4099. if Delimited then
  4100. if (p < TemplateEnd) and (p^ = '}') then
  4101. Inc(p) // skip right curly brace
  4102. else
  4103. p := APtr; // isn't properly terminated
  4104. if p = APtr then
  4105. Result := -1; // no valid digits found or no right curly brace
  4106. APtr := p;
  4107. end;
  4108. type
  4109. TSubstMode = (smodeNormal, smodeOneUpper, smodeOneLower, smodeAllUpper, smodeAllLower);
  4110. var
  4111. Mode: TSubstMode;
  4112. p, p0, p1, ResultPtr: PRegExprChar;
  4113. ResultLen, n: integer;
  4114. Ch, QuotedChar: REChar;
  4115. begin
  4116. // Check programm and input string
  4117. if not IsProgrammOk then
  4118. Exit;
  4119. if fInputString = '' then
  4120. begin
  4121. if EmptyInputRaisesError then
  4122. Error(reeNoInputStringSpecified);
  4123. Exit;
  4124. end;
  4125. // Prepare for working
  4126. if ATemplate = '' then
  4127. begin // prevent nil pointers
  4128. Result := '';
  4129. Exit;
  4130. end;
  4131. TemplateBeg := PRegExprChar(ATemplate);
  4132. TemplateEnd := TemplateBeg + Length(ATemplate);
  4133. // Count result length for speed optimization.
  4134. ResultLen := 0;
  4135. p := TemplateBeg;
  4136. while p < TemplateEnd do
  4137. begin
  4138. Ch := p^;
  4139. Inc(p);
  4140. n := -1;
  4141. if Ch = '$' then
  4142. begin
  4143. n := ParseVarName(p);
  4144. if (n >= 0) and (n <= High(GrpIndexes)) then
  4145. n := GrpIndexes[n];
  4146. end;
  4147. if n >= 0 then
  4148. begin
  4149. Inc(ResultLen, endp[n] - startp[n]);
  4150. end
  4151. else
  4152. begin
  4153. if (Ch = EscChar) and (p < TemplateEnd) then
  4154. begin // quoted or special char followed
  4155. Ch := p^;
  4156. Inc(p);
  4157. case Ch of
  4158. 'n':
  4159. Inc(ResultLen, Length(FReplaceLineEnd));
  4160. 'u', 'l', 'U', 'L': { nothing }
  4161. ;
  4162. 'x':
  4163. begin
  4164. Inc(ResultLen);
  4165. if (p^ = '{') then
  4166. begin // skip \x{....}
  4167. while ((p^ <> '}') and (p < TemplateEnd)) do
  4168. p := p + 1;
  4169. p := p + 1;
  4170. end
  4171. else
  4172. p := p + 2 // skip \x..
  4173. end;
  4174. else
  4175. Inc(ResultLen);
  4176. end;
  4177. end
  4178. else
  4179. Inc(ResultLen);
  4180. end;
  4181. end;
  4182. // Get memory. We do it once and it significant speed up work !
  4183. if ResultLen = 0 then
  4184. begin
  4185. Result := '';
  4186. Exit;
  4187. end;
  4188. SetLength(Result, ResultLen);
  4189. // Fill Result
  4190. ResultPtr := PRegExprChar(Result);
  4191. p := TemplateBeg;
  4192. Mode := smodeNormal;
  4193. while p < TemplateEnd do
  4194. begin
  4195. Ch := p^;
  4196. p0 := p;
  4197. Inc(p);
  4198. p1 := p;
  4199. n := -1;
  4200. if Ch = '$' then
  4201. begin
  4202. n := ParseVarName(p);
  4203. if (n >= 0) and (n <= High(GrpIndexes)) then
  4204. n := GrpIndexes[n];
  4205. end;
  4206. if (n >= 0) then
  4207. begin
  4208. p0 := startp[n];
  4209. p1 := endp[n];
  4210. end
  4211. else
  4212. begin
  4213. if (Ch = EscChar) and (p < TemplateEnd) then
  4214. begin // quoted or special char followed
  4215. Ch := p^;
  4216. Inc(p);
  4217. case Ch of
  4218. 'n':
  4219. begin
  4220. p0 := PRegExprChar(FReplaceLineEnd);
  4221. p1 := p0 + Length(FReplaceLineEnd);
  4222. end;
  4223. 'x', 't', 'r', 'f', 'a', 'e':
  4224. begin
  4225. p := p - 1;
  4226. // UnquoteChar expects the escaped char under the pointer
  4227. QuotedChar := UnQuoteChar(p);
  4228. p := p + 1;
  4229. // Skip after last part of the escaped sequence - UnquoteChar stops on the last symbol of it
  4230. p0 := @QuotedChar;
  4231. p1 := p0 + 1;
  4232. end;
  4233. 'l':
  4234. begin
  4235. Mode := smodeOneLower;
  4236. p1 := p0;
  4237. end;
  4238. 'L':
  4239. begin
  4240. Mode := smodeAllLower;
  4241. p1 := p0;
  4242. end;
  4243. 'u':
  4244. begin
  4245. Mode := smodeOneUpper;
  4246. p1 := p0;
  4247. end;
  4248. 'U':
  4249. begin
  4250. Mode := smodeAllUpper;
  4251. p1 := p0;
  4252. end;
  4253. else
  4254. begin
  4255. Inc(p0);
  4256. Inc(p1);
  4257. end;
  4258. end;
  4259. end
  4260. end;
  4261. if p0 < p1 then
  4262. begin
  4263. while p0 < p1 do
  4264. begin
  4265. case Mode of
  4266. smodeOneLower:
  4267. begin
  4268. ResultPtr^ := _LowerCase(p0^);
  4269. Mode := smodeNormal;
  4270. end;
  4271. smodeAllLower:
  4272. begin
  4273. ResultPtr^ := _LowerCase(p0^);
  4274. end;
  4275. smodeOneUpper:
  4276. begin
  4277. ResultPtr^ := _UpperCase(p0^);
  4278. Mode := smodeNormal;
  4279. end;
  4280. smodeAllUpper:
  4281. begin
  4282. ResultPtr^ := _UpperCase(p0^);
  4283. end;
  4284. else
  4285. ResultPtr^ := p0^;
  4286. end;
  4287. Inc(ResultPtr);
  4288. Inc(p0);
  4289. end;
  4290. Mode := smodeNormal;
  4291. end;
  4292. end;
  4293. end; { of function TRegExpr.Substitute
  4294. -------------------------------------------------------------- }
  4295. procedure TRegExpr.Split(const AInputStr: RegExprString; APieces: TStrings);
  4296. var
  4297. PrevPos: PtrInt;
  4298. begin
  4299. PrevPos := 1;
  4300. if Exec(AInputStr) then
  4301. repeat
  4302. APieces.Add(System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos));
  4303. PrevPos := MatchPos[0] + MatchLen[0];
  4304. until not ExecNext;
  4305. APieces.Add(System.Copy(AInputStr, PrevPos, MaxInt)); // Tail
  4306. end; { of procedure TRegExpr.Split
  4307. -------------------------------------------------------------- }
  4308. function TRegExpr.Replace(const AInputStr: RegExprString;
  4309. const AReplaceStr: RegExprString;
  4310. AUseSubstitution: boolean = False): RegExprString;
  4311. var
  4312. PrevPos: PtrInt;
  4313. begin
  4314. Result := '';
  4315. PrevPos := 1;
  4316. if Exec(AInputStr) then
  4317. repeat
  4318. Result := Result + System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos);
  4319. if AUseSubstitution // ###0.946
  4320. then
  4321. Result := Result + Substitute(AReplaceStr)
  4322. else
  4323. Result := Result + AReplaceStr;
  4324. PrevPos := MatchPos[0] + MatchLen[0];
  4325. until not ExecNext;
  4326. Result := Result + System.Copy(AInputStr, PrevPos, MaxInt); // Tail
  4327. end; { of function TRegExpr.Replace
  4328. -------------------------------------------------------------- }
  4329. function TRegExpr.ReplaceEx(const AInputStr: RegExprString;
  4330. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  4331. var
  4332. PrevPos: PtrInt;
  4333. begin
  4334. Result := '';
  4335. PrevPos := 1;
  4336. if Exec(AInputStr) then
  4337. repeat
  4338. Result := Result + System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos)
  4339. + AReplaceFunc(Self);
  4340. PrevPos := MatchPos[0] + MatchLen[0];
  4341. until not ExecNext;
  4342. Result := Result + System.Copy(AInputStr, PrevPos, MaxInt); // Tail
  4343. end; { of function TRegExpr.ReplaceEx
  4344. -------------------------------------------------------------- }
  4345. function TRegExpr.Replace(const AInputStr: RegExprString;
  4346. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  4347. begin
  4348. Result := ReplaceEx(AInputStr, AReplaceFunc);
  4349. end; { of function TRegExpr.Replace
  4350. -------------------------------------------------------------- }
  4351. { ============================================================= }
  4352. { ====================== Debug section ======================== }
  4353. { ============================================================= }
  4354. {$IFDEF UseFirstCharSet}
  4355. procedure TRegExpr.FillFirstCharSet(prog: PRegExprChar);
  4356. var
  4357. scan: PRegExprChar; // Current node.
  4358. Next: PRegExprChar; // Next node.
  4359. opnd: PRegExprChar;
  4360. Oper: TREOp;
  4361. ch: REChar;
  4362. min_cnt, i: integer;
  4363. TempSet: TRegExprCharset;
  4364. begin
  4365. TempSet := [];
  4366. scan := prog;
  4367. while scan <> nil do
  4368. begin
  4369. Next := regnext(scan);
  4370. Oper := PREOp(scan)^;
  4371. case Oper of
  4372. OP_BSUBEXP,
  4373. OP_BSUBEXPCI:
  4374. begin
  4375. // we cannot optimize r.e. if it starts with back reference
  4376. FirstCharSet := RegExprAllSet; //###0.930
  4377. Exit;
  4378. end;
  4379. OP_BOL,
  4380. OP_BOLML:
  4381. ; // Exit; //###0.937
  4382. OP_EOL,
  4383. OP_EOLML:
  4384. begin //###0.948 was empty in 0.947, was EXIT in 0.937
  4385. Include(FirstCharSet, 0);
  4386. if ModifierM then
  4387. for i := 1 to Length(LineSeparators) do
  4388. Include(FirstCharSet, byte(LineSeparators[i]));
  4389. Exit;
  4390. end;
  4391. OP_BOUND,
  4392. OP_NOTBOUND:
  4393. ; //###0.943 ?!!
  4394. OP_ANY,
  4395. OP_ANYML:
  4396. begin // we can better define ANYML !!!
  4397. FirstCharSet := RegExprAllSet; //###0.930
  4398. Exit;
  4399. end;
  4400. OP_ANYDIGIT:
  4401. begin
  4402. FirstCharSet := FirstCharSet + RegExprDigitSet;
  4403. Exit;
  4404. end;
  4405. OP_NOTDIGIT:
  4406. begin
  4407. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprDigitSet);
  4408. Exit;
  4409. end;
  4410. OP_ANYLETTER:
  4411. begin
  4412. GetCharSetFromWordChars(TempSet);
  4413. FirstCharSet := FirstCharSet + TempSet;
  4414. Exit;
  4415. end;
  4416. OP_NOTLETTER:
  4417. begin
  4418. GetCharSetFromWordChars(TempSet);
  4419. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4420. Exit;
  4421. end;
  4422. OP_ANYSPACE:
  4423. begin
  4424. GetCharSetFromSpaceChars(TempSet);
  4425. FirstCharSet := FirstCharSet + TempSet;
  4426. Exit;
  4427. end;
  4428. OP_NOTSPACE:
  4429. begin
  4430. GetCharSetFromSpaceChars(TempSet);
  4431. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4432. Exit;
  4433. end;
  4434. OP_ANYVERTSEP:
  4435. begin
  4436. FirstCharSet := FirstCharSet + RegExprLineSeparatorsSet;
  4437. Exit;
  4438. end;
  4439. OP_NOTVERTSEP:
  4440. begin
  4441. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprLineSeparatorsSet);
  4442. Exit;
  4443. end;
  4444. OP_ANYHORZSEP:
  4445. begin
  4446. FirstCharSet := FirstCharSet + RegExprHorzSeparatorsSet;
  4447. Exit;
  4448. end;
  4449. OP_NOTHORZSEP:
  4450. begin
  4451. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprHorzSeparatorsSet);
  4452. Exit;
  4453. end;
  4454. OP_EXACTLYCI:
  4455. begin
  4456. ch := (scan + REOpSz + RENextOffSz + RENumberSz)^;
  4457. {$IFDEF UniCode}
  4458. if Ord(ch) <= $FF then
  4459. {$ENDIF}
  4460. begin
  4461. Include(FirstCharSet, byte(ch));
  4462. Include(FirstCharSet, byte(InvertCase(ch)));
  4463. end;
  4464. Exit;
  4465. end;
  4466. OP_EXACTLY:
  4467. begin
  4468. ch := (scan + REOpSz + RENextOffSz + RENumberSz)^;
  4469. {$IFDEF UniCode}
  4470. if Ord(ch) <= $FF then
  4471. {$ENDIF}
  4472. Include(FirstCharSet, byte(ch));
  4473. Exit;
  4474. end;
  4475. OP_ANYOF:
  4476. begin
  4477. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, False, TempSet);
  4478. FirstCharSet := FirstCharSet + TempSet;
  4479. Exit;
  4480. end;
  4481. OP_ANYBUT:
  4482. begin
  4483. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, False, TempSet);
  4484. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4485. Exit;
  4486. end;
  4487. OP_ANYOFCI:
  4488. begin
  4489. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, True, TempSet);
  4490. FirstCharSet := FirstCharSet + TempSet;
  4491. Exit;
  4492. end;
  4493. OP_ANYBUTCI:
  4494. begin
  4495. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, True, TempSet);
  4496. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4497. Exit;
  4498. end;
  4499. OP_NOTHING:
  4500. ;
  4501. OP_COMMENT:
  4502. ;
  4503. OP_BACK:
  4504. ;
  4505. Succ(OP_OPEN) .. TREOp(Ord(OP_OPEN) + NSUBEXP - 1):
  4506. begin //###0.929
  4507. FillFirstCharSet(Next);
  4508. Exit;
  4509. end;
  4510. Succ(OP_CLOSE) .. TREOp(Ord(OP_CLOSE) + NSUBEXP - 1):
  4511. begin //###0.929
  4512. FillFirstCharSet(Next);
  4513. Exit;
  4514. end;
  4515. OP_BRANCH:
  4516. begin
  4517. if (PREOp(Next)^ <> OP_BRANCH) // No choice.
  4518. then
  4519. Next := scan + REOpSz + RENextOffSz // Avoid recursion.
  4520. else
  4521. begin
  4522. repeat
  4523. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  4524. scan := regnext(scan);
  4525. until (scan = nil) or (PREOp(scan)^ <> OP_BRANCH);
  4526. Exit;
  4527. end;
  4528. end;
  4529. {$IFDEF ComplexBraces}
  4530. OP_LOOPENTRY:
  4531. begin //###0.925
  4532. //LoopStack [LoopStackIdx] := 0; //###0.940 line removed
  4533. FillFirstCharSet(Next); // execute LOOP
  4534. Exit;
  4535. end;
  4536. OP_LOOP,
  4537. OP_LOOPNG:
  4538. begin //###0.940
  4539. opnd := scan + PRENextOff(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz * 2))^;
  4540. min_cnt := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  4541. FillFirstCharSet(opnd);
  4542. if min_cnt = 0 then
  4543. FillFirstCharSet(Next);
  4544. Exit;
  4545. end;
  4546. {$ENDIF}
  4547. OP_STAR,
  4548. OP_STARNG: //###0.940
  4549. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  4550. OP_PLUS,
  4551. OP_PLUSNG:
  4552. begin //###0.940
  4553. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  4554. Exit;
  4555. end;
  4556. OP_BRACES,
  4557. OP_BRACESNG:
  4558. begin //###0.940
  4559. opnd := scan + REOpSz + RENextOffSz + REBracesArgSz * 2;
  4560. min_cnt := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^; // BRACES
  4561. FillFirstCharSet(opnd);
  4562. if min_cnt > 0 then
  4563. Exit;
  4564. end;
  4565. OP_EEND:
  4566. begin
  4567. FirstCharSet := RegExprAllSet; //###0.948
  4568. Exit;
  4569. end;
  4570. else
  4571. begin
  4572. fLastErrorOpcode := Oper;
  4573. Error(reeUnknownOpcodeInFillFirst);
  4574. Exit;
  4575. end;
  4576. end; { of case scan^}
  4577. scan := Next;
  4578. end; { of while scan <> nil}
  4579. end; { of procedure FillFirstCharSet
  4580. --------------------------------------------------------------}
  4581. {$ENDIF}
  4582. procedure TRegExpr.InitCharCheckers;
  4583. var
  4584. Cnt: integer;
  4585. //
  4586. function Add(AChecker: TRegExprCharChecker): byte;
  4587. begin
  4588. Inc(Cnt);
  4589. if Cnt > High(CharCheckers) then
  4590. raise Exception.Create('Too small CharCheckers array');
  4591. CharCheckers[Cnt - 1] := AChecker;
  4592. Result := Cnt - 1;
  4593. end;
  4594. //
  4595. begin
  4596. Cnt := 0;
  4597. FillChar(CharCheckers, SizeOf(CharCheckers), 0);
  4598. CheckerIndex_Word := Add(CharChecker_Word);
  4599. CheckerIndex_NotWord := Add(CharChecker_NotWord);
  4600. CheckerIndex_Space := Add(CharChecker_Space);
  4601. CheckerIndex_NotSpace := Add(CharChecker_NotSpace);
  4602. CheckerIndex_Digit := Add(CharChecker_Digit);
  4603. CheckerIndex_NotDigit := Add(CharChecker_NotDigit);
  4604. CheckerIndex_VertSep := Add(CharChecker_VertSep);
  4605. CheckerIndex_NotVertSep := Add(CharChecker_NotVertSep);
  4606. CheckerIndex_HorzSep := Add(CharChecker_HorzSep);
  4607. CheckerIndex_NotHorzSep := Add(CharChecker_NotHorzSep);
  4608. //CheckerIndex_AllAZ := Add(CharChecker_AllAZ);
  4609. CheckerIndex_LowerAZ := Add(CharChecker_LowerAZ);
  4610. CheckerIndex_UpperAZ := Add(CharChecker_UpperAZ);
  4611. SetLength(CharCheckerInfos, 3);
  4612. with CharCheckerInfos[0] do
  4613. begin
  4614. CharBegin := 'a';
  4615. CharEnd:= 'z';
  4616. CheckerIndex := CheckerIndex_LowerAZ;
  4617. end;
  4618. with CharCheckerInfos[1] do
  4619. begin
  4620. CharBegin := 'A';
  4621. CharEnd := 'Z';
  4622. CheckerIndex := CheckerIndex_UpperAZ;
  4623. end;
  4624. with CharCheckerInfos[2] do
  4625. begin
  4626. CharBegin := '0';
  4627. CharEnd := '9';
  4628. CheckerIndex := CheckerIndex_Digit;
  4629. end;
  4630. end;
  4631. function TRegExpr.CharChecker_Word(ch: REChar): boolean;
  4632. begin
  4633. Result := IsWordChar(ch);
  4634. end;
  4635. function TRegExpr.CharChecker_NotWord(ch: REChar): boolean;
  4636. begin
  4637. Result := not IsWordChar(ch);
  4638. end;
  4639. function TRegExpr.CharChecker_Space(ch: REChar): boolean;
  4640. begin
  4641. Result := IsSpaceChar(ch);
  4642. end;
  4643. function TRegExpr.CharChecker_NotSpace(ch: REChar): boolean;
  4644. begin
  4645. Result := not IsSpaceChar(ch);
  4646. end;
  4647. function TRegExpr.CharChecker_Digit(ch: REChar): boolean;
  4648. begin
  4649. Result := IsDigitChar(ch);
  4650. end;
  4651. function TRegExpr.CharChecker_NotDigit(ch: REChar): boolean;
  4652. begin
  4653. Result := not IsDigitChar(ch);
  4654. end;
  4655. function TRegExpr.CharChecker_VertSep(ch: REChar): boolean;
  4656. begin
  4657. Result := IsLineSeparator(ch);
  4658. end;
  4659. function TRegExpr.CharChecker_NotVertSep(ch: REChar): boolean;
  4660. begin
  4661. Result := not IsLineSeparator(ch);
  4662. end;
  4663. function TRegExpr.CharChecker_HorzSep(ch: REChar): boolean;
  4664. begin
  4665. Result := IsHorzSeparator(ch);
  4666. end;
  4667. function TRegExpr.CharChecker_NotHorzSep(ch: REChar): boolean;
  4668. begin
  4669. Result := not IsHorzSeparator(ch);
  4670. end;
  4671. function TRegExpr.CharChecker_LowerAZ(ch: REChar): boolean;
  4672. begin
  4673. case ch of
  4674. 'a' .. 'z':
  4675. Result := True;
  4676. else
  4677. Result := False;
  4678. end;
  4679. end;
  4680. function TRegExpr.CharChecker_UpperAZ(ch: REChar): boolean;
  4681. begin
  4682. case ch of
  4683. 'A' .. 'Z':
  4684. Result := True;
  4685. else
  4686. Result := False;
  4687. end;
  4688. end;
  4689. {$IFDEF RegExpPCodeDump}
  4690. function TRegExpr.DumpOp(op: TREOp): RegExprString;
  4691. // printable representation of opcode
  4692. begin
  4693. case op of
  4694. OP_BOL:
  4695. Result := 'BOL';
  4696. OP_EOL:
  4697. Result := 'EOL';
  4698. OP_BOLML:
  4699. Result := 'BOLML';
  4700. OP_EOLML:
  4701. Result := 'EOLML';
  4702. OP_BOUND:
  4703. Result := 'BOUND'; // ###0.943
  4704. OP_NOTBOUND:
  4705. Result := 'NOTBOUND'; // ###0.943
  4706. OP_ANY:
  4707. Result := 'ANY';
  4708. OP_ANYML:
  4709. Result := 'ANYML'; // ###0.941
  4710. OP_ANYLETTER:
  4711. Result := 'ANYLETTER';
  4712. OP_NOTLETTER:
  4713. Result := 'NOTLETTER';
  4714. OP_ANYDIGIT:
  4715. Result := 'ANYDIGIT';
  4716. OP_NOTDIGIT:
  4717. Result := 'NOTDIGIT';
  4718. OP_ANYSPACE:
  4719. Result := 'ANYSPACE';
  4720. OP_NOTSPACE:
  4721. Result := 'NOTSPACE';
  4722. OP_ANYHORZSEP:
  4723. Result := 'ANYHORZSEP';
  4724. OP_NOTHORZSEP:
  4725. Result := 'NOTHORZSEP';
  4726. OP_ANYVERTSEP:
  4727. Result := 'ANYVERTSEP';
  4728. OP_NOTVERTSEP:
  4729. Result := 'NOTVERTSEP';
  4730. OP_ANYOF:
  4731. Result := 'ANYOF';
  4732. OP_ANYBUT:
  4733. Result := 'ANYBUT';
  4734. OP_ANYOFCI:
  4735. Result := 'ANYOF/CI';
  4736. OP_ANYBUTCI:
  4737. Result := 'ANYBUT/CI';
  4738. OP_BRANCH:
  4739. Result := 'BRANCH';
  4740. OP_EXACTLY:
  4741. Result := 'EXACTLY';
  4742. OP_EXACTLYCI:
  4743. Result := 'EXACTLY/CI';
  4744. OP_NOTHING:
  4745. Result := 'NOTHING';
  4746. OP_COMMENT:
  4747. Result := 'COMMENT';
  4748. OP_BACK:
  4749. Result := 'BACK';
  4750. OP_EEND:
  4751. Result := 'END';
  4752. OP_BSUBEXP:
  4753. Result := 'BSUBEXP';
  4754. OP_BSUBEXPCI:
  4755. Result := 'BSUBEXP/CI';
  4756. Succ(OP_OPEN) .. TREOp(Ord(OP_OPEN) + NSUBEXP - 1): // ###0.929
  4757. Result := Format('OPEN[%d]', [Ord(op) - Ord(OP_OPEN)]);
  4758. Succ(OP_CLOSE) .. TREOp(Ord(OP_CLOSE) + NSUBEXP - 1): // ###0.929
  4759. Result := Format('CLOSE[%d]', [Ord(op) - Ord(OP_CLOSE)]);
  4760. OP_STAR:
  4761. Result := 'STAR';
  4762. OP_PLUS:
  4763. Result := 'PLUS';
  4764. OP_BRACES:
  4765. Result := 'BRACES';
  4766. {$IFDEF ComplexBraces}
  4767. OP_LOOPENTRY:
  4768. Result := 'LOOPENTRY'; // ###0.925
  4769. OP_LOOP:
  4770. Result := 'LOOP'; // ###0.925
  4771. OP_LOOPNG:
  4772. Result := 'LOOPNG'; // ###0.940
  4773. {$ENDIF}
  4774. OP_STARNG:
  4775. Result := 'STARNG'; // ###0.940
  4776. OP_PLUSNG:
  4777. Result := 'PLUSNG'; // ###0.940
  4778. OP_BRACESNG:
  4779. Result := 'BRACESNG'; // ###0.940
  4780. else
  4781. Error(reeDumpCorruptedOpcode);
  4782. end; { of case op }
  4783. Result := ':' + Result;
  4784. end; { of function TRegExpr.DumpOp
  4785. -------------------------------------------------------------- }
  4786. function TRegExpr.Dump: RegExprString;
  4787. // dump a regexp in vaguely comprehensible form
  4788. var
  4789. s: PRegExprChar;
  4790. op: TREOp; // Arbitrary non-END op.
  4791. next: PRegExprChar;
  4792. i, NLen: integer;
  4793. Diff: PtrInt;
  4794. Ch: AnsiChar;
  4795. function PrintableChar(AChar: REChar): string; {$IFDEF InlineFuncs}inline;{$ENDIF}
  4796. begin
  4797. if AChar < ' ' then
  4798. Result := '#' + IntToStr(Ord(AChar))
  4799. else
  4800. Result := AChar;
  4801. end;
  4802. begin
  4803. if not IsProgrammOk then
  4804. Exit;
  4805. op := OP_EXACTLY;
  4806. Result := '';
  4807. s := programm + REOpSz;
  4808. while op <> OP_EEND do
  4809. begin // While that wasn't END last time...
  4810. op := s^;
  4811. Result := Result + Format('%2d%s', [s - programm, DumpOp(s^)]);
  4812. // Where, what.
  4813. next := regnext(s);
  4814. if next = nil // Next ptr.
  4815. then
  4816. Result := Result + ' (0)'
  4817. else
  4818. begin
  4819. if next > s
  4820. // ###0.948 PWideChar subtraction workaround (see comments in Tail method for details)
  4821. then
  4822. Diff := next - s
  4823. else
  4824. Diff := -(s - next);
  4825. Result := Result + Format(' (%d) ', [(s - programm) + Diff]);
  4826. end;
  4827. Inc(s, REOpSz + RENextOffSz);
  4828. if (op = OP_ANYOF) or (op = OP_ANYOFCI) or (op = OP_ANYBUT) or (op = OP_ANYBUTCI) then
  4829. begin
  4830. repeat
  4831. case s^ of
  4832. OpKind_End:
  4833. begin
  4834. Inc(s);
  4835. Break;
  4836. end;
  4837. OpKind_Range:
  4838. begin
  4839. Result := Result + 'Rng(';
  4840. Inc(s);
  4841. Result := Result + PrintableChar(s^) + '-';
  4842. Inc(s);
  4843. Result := Result + PrintableChar(s^);
  4844. Result := Result + ') ';
  4845. Inc(s);
  4846. end;
  4847. OpKind_MetaClass:
  4848. begin
  4849. Inc(s);
  4850. Result := Result + '\' + PrintableChar(s^) + ' ';
  4851. Inc(s);
  4852. end;
  4853. OpKind_Char:
  4854. begin
  4855. Inc(s);
  4856. NLen := PLongInt(s)^;
  4857. Inc(s, RENumberSz);
  4858. Result := Result + 'Ch(';
  4859. for i := 1 to NLen do
  4860. begin
  4861. Result := Result + PrintableChar(s^);
  4862. Inc(s);
  4863. end;
  4864. Result := Result + ') ';
  4865. end;
  4866. else
  4867. Error(reeDumpCorruptedOpcode);
  4868. end;
  4869. until false;
  4870. end;
  4871. if (op = OP_EXACTLY) or (op = OP_EXACTLYCI) then
  4872. begin
  4873. // Literal string, where present.
  4874. NLen := PLongInt(s)^;
  4875. Inc(s, RENumberSz);
  4876. for i := 1 to NLen do
  4877. begin
  4878. Result := Result + PrintableChar(s^);
  4879. Inc(s);
  4880. end;
  4881. end;
  4882. if (op = OP_BSUBEXP) or (op = OP_BSUBEXPCI) then
  4883. begin
  4884. Result := Result + ' \' + IntToStr(Ord(s^));
  4885. Inc(s);
  4886. end;
  4887. if (op = OP_BRACES) or (op = OP_BRACESNG) then
  4888. begin // ###0.941
  4889. // show min/max argument of braces operator
  4890. Result := Result + Format('{%d,%d}', [PREBracesArg(AlignToInt(s))^,
  4891. PREBracesArg(AlignToInt(s + REBracesArgSz))^]);
  4892. Inc(s, REBracesArgSz * 2);
  4893. end;
  4894. {$IFDEF ComplexBraces}
  4895. if (op = OP_LOOP) or (op = OP_LOOPNG) then
  4896. begin // ###0.940
  4897. Result := Result + Format(' -> (%d) {%d,%d}',
  4898. [(s - programm - (REOpSz + RENextOffSz)) +
  4899. PRENextOff(AlignToPtr(s + 2 * REBracesArgSz))^,
  4900. PREBracesArg(AlignToInt(s))^,
  4901. PREBracesArg(AlignToInt(s + REBracesArgSz))^]);
  4902. Inc(s, 2 * REBracesArgSz + RENextOffSz);
  4903. end;
  4904. {$ENDIF}
  4905. Result := Result + #$d#$a;
  4906. end; { of while }
  4907. // Header fields of interest.
  4908. if reganchored <> #0 then
  4909. Result := Result + 'Anchored; ';
  4910. if regmustString <> '' then
  4911. Result := Result + 'Must have: "' + regmustString + '"; ';
  4912. {$IFDEF UseFirstCharSet} // ###0.929
  4913. Result := Result + #$d#$a'First charset: ';
  4914. if FirstCharSet = [] then
  4915. Result := Result + '<empty set>'
  4916. else
  4917. if FirstCharSet = RegExprAllSet then
  4918. Result := Result + '<all chars>'
  4919. else
  4920. for Ch := #0 to #255 do
  4921. if byte(Ch) in FirstCharSet then
  4922. begin
  4923. if Ch < ' ' then
  4924. Result := Result + PrintableChar(Ch) // ###0.948
  4925. else
  4926. Result := Result + Ch;
  4927. end;
  4928. {$ENDIF}
  4929. Result := Result + #$d#$a;
  4930. end; { of function TRegExpr.Dump
  4931. -------------------------------------------------------------- }
  4932. {$ENDIF}
  4933. {$IFDEF reRealExceptionAddr}
  4934. {$OPTIMIZATION ON}
  4935. // ReturnAddr works correctly only if compiler optimization is ON
  4936. // I placed this method at very end of unit because there are no
  4937. // way to restore compiler optimization flag ...
  4938. {$ENDIF}
  4939. procedure TRegExpr.Error(AErrorID: integer);
  4940. {$IFDEF reRealExceptionAddr}
  4941. function ReturnAddr: Pointer; // ###0.938
  4942. asm
  4943. mov eax,[ebp+4]
  4944. end;
  4945. {$ENDIF}
  4946. var
  4947. e: ERegExpr;
  4948. begin
  4949. fLastError := AErrorID; // dummy stub - useless because will raise exception
  4950. if AErrorID < 1000 // compilation error ?
  4951. then
  4952. e := ERegExpr.Create(ErrorMsg(AErrorID) // yes - show error pos
  4953. + ' (pos ' + IntToStr(CompilerErrorPos) + ')')
  4954. else
  4955. e := ERegExpr.Create(ErrorMsg(AErrorID));
  4956. e.ErrorCode := AErrorID;
  4957. e.CompilerErrorPos := CompilerErrorPos;
  4958. raise e
  4959. {$IFDEF reRealExceptionAddr}
  4960. at ReturnAddr; // ###0.938
  4961. {$ENDIF}
  4962. end; { of procedure TRegExpr.Error
  4963. -------------------------------------------------------------- }
  4964. (*
  4965. PCode persistence:
  4966. FirstCharSet
  4967. programm, regsize
  4968. reganchored // -> programm
  4969. regmust, regmustlen // -> programm
  4970. fExprIsCompiled
  4971. *)
  4972. // be carefull - placed here code will be always compiled with
  4973. // compiler optimization flag
  4974. initialization
  4975. RegExprInvertCaseFunction := TRegExpr.InvertCaseFunction;
  4976. end.