regexpr.pas 146 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241
  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. http://RegExpStudio.com
  21. mailto:[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. mailto:[email protected]
  32. http://RegExpStudio.com
  33. http://anso.da.ru/
  34. Option 2>
  35. The same modified LGPL with static linking exception as the Free Pascal RTL
  36. }
  37. interface
  38. {off $DEFINE DebugSynRegExpr}
  39. {$IFDEF FPC}
  40. {$MODE DELPHI} // Delphi-compatible mode in FreePascal
  41. {$INLINE ON}
  42. {$ENDIF}
  43. // ======== Determine compiler
  44. {$IFDEF VER80} Sorry, TRegExpr is for 32-bits Delphi only. Delphi 1 is not supported (and whos really care today?!). {$ENDIF}
  45. {$IFDEF VER90} {$DEFINE D2} {$ENDIF} // D2
  46. {$IFDEF VER93} {$DEFINE D2} {$ENDIF} // CPPB 1
  47. {$IFDEF VER100} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D3
  48. {$IFDEF VER110} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // CPPB 3
  49. {$IFDEF VER120} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D4
  50. {$IFDEF VER130} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D5
  51. {$IFDEF VER140} {$DEFINE D6} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D6
  52. {$IFDEF VER150} {$DEFINE D7} {$DEFINE D6} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D7
  53. // ======== Define base compiler options
  54. {$BOOLEVAL OFF}
  55. {$EXTENDEDSYNTAX ON}
  56. {$LONGSTRINGS ON}
  57. {$IFNDEF FPC}
  58. {$OPTIMIZATION ON}
  59. {$ENDIF}
  60. {$IFDEF D6}
  61. {$WARN SYMBOL_PLATFORM OFF} // Suppress .Net warnings
  62. {$ENDIF}
  63. {$IFDEF D7}
  64. {$WARN UNSAFE_CAST OFF} // Suppress .Net warnings
  65. {$WARN UNSAFE_TYPE OFF} // Suppress .Net warnings
  66. {$WARN UNSAFE_CODE OFF} // Suppress .Net warnings
  67. {$ENDIF}
  68. // ======== Define options for TRegExpr engine
  69. {.$DEFINE UniCode} // Unicode support
  70. {$ifdef FPC_OS_UNICODE}
  71. {$define UNICODE}
  72. {$endif}
  73. {$DEFINE RegExpPCodeDump} // p-code dumping (see Dump method)
  74. {$IFNDEF FPC} // the option is not supported in FreePascal
  75. {$DEFINE reRealExceptionAddr} // exceptions will point to appropriate source line, not to Error procedure
  76. {$ENDIF}
  77. {$DEFINE ComplexBraces} // support braces in complex cases
  78. {$IFNDEF UniCode} // the option applicable only for non-UniCode mode
  79. {$IFNDEF FPC_REQUIRES_PROPER_ALIGNMENT} //sets have to be aligned
  80. {$DEFINE UseSetOfChar} // Significant optimization by using set of char
  81. {$ENDIF}
  82. {$ENDIF}
  83. {$IFDEF UseSetOfChar}
  84. {$DEFINE UseFirstCharSet} // Fast skip between matches for r.e. that starts with determined set of chars
  85. {$ENDIF}
  86. {$DEFINE UseOsLineEndOnReplace} // On Replace if replace-with has "\n", use System.LineEnding (#10 #13 or #13#10); else use #10
  87. // ======== Define Pascal-language options
  88. // Define 'UseAsserts' option (do not edit this definitions).
  89. // Asserts used to catch 'strange bugs' in TRegExpr implementation (when something goes
  90. // completely wrong). You can swith asserts on/off with help of {$C+}/{$C-} compiler options.
  91. {$IFDEF D3} {$DEFINE UseAsserts} {$ENDIF}
  92. {$IFDEF FPC} {$DEFINE UseAsserts} {$ENDIF}
  93. // Define 'use subroutine parameters default values' option (do not edit this definition).
  94. {$IFDEF D4} {$DEFINE DefParam} {$ENDIF}
  95. // Define 'OverMeth' options, to use method overloading (do not edit this definitions).
  96. {$IFDEF D5} {$DEFINE OverMeth} {$ENDIF}
  97. {$IFDEF FPC} {$DEFINE OverMeth} {$ENDIF}
  98. uses
  99. Classes, // TStrings in Split method
  100. SysUtils; // Exception
  101. type
  102. {$IFDEF UniCode}
  103. PRegExprChar = PWideChar;
  104. RegExprString = UnicodeString;
  105. REChar = WideChar;
  106. {$ELSE}
  107. PRegExprChar = PChar;
  108. RegExprString = AnsiString; //###0.952 was string
  109. REChar = Char;
  110. {$ENDIF}
  111. TREOp = REChar; // internal p-code type //###0.933
  112. PREOp = ^TREOp;
  113. TRENextOff = PtrInt; // internal Next "pointer" (offset to current p-code) //###0.933
  114. PRENextOff = ^TRENextOff; // used for extracting Next "pointers" from compiled r.e. //###0.933
  115. TREBracesArg = integer; // type of {m,n} arguments
  116. PREBracesArg = ^TREBracesArg;
  117. const
  118. REOpSz = SizeOf (TREOp) div SizeOf (REChar); // size of p-code in RegExprString units
  119. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  120. // add space for aligning pointer
  121. // -1 is the correct max size but also needed for InsertOperator that needs a multiple of pointer size
  122. RENextOffSz = (2 * SizeOf (TRENextOff) div SizeOf (REChar))-1;
  123. REBracesArgSz = (2 * SizeOf (TREBracesArg) div SizeOf (REChar)); // add space for aligning pointer
  124. {$ELSE}
  125. RENextOffSz = (SizeOf (TRENextOff) div SizeOf (REChar)); // size of Next 'pointer' -"-
  126. REBracesArgSz = SizeOf (TREBracesArg) div SizeOf (REChar); // size of BRACES arguments -"-
  127. {$ENDIF}
  128. type
  129. TRegExprInvertCaseFunction = function (const Ch : REChar) : REChar
  130. of object;
  131. const
  132. EscChar = '\'; // 'Escape'-char ('\' in common r.e.) used for escaping metachars (\w, \d etc).
  133. RegExprModifierI : boolean = False; // default value for ModifierI
  134. RegExprModifierR : boolean = True; // default value for ModifierR
  135. RegExprModifierS : boolean = True; // default value for ModifierS
  136. RegExprModifierG : boolean = True; // default value for ModifierG
  137. RegExprModifierM : boolean = False; // default value for ModifierM
  138. RegExprModifierX : boolean = False; // default value for ModifierX
  139. RegExprSpaceChars : RegExprString = // default value for SpaceChars
  140. ' '#$9#$A#$D#$C;
  141. RegExprWordChars : RegExprString = // default value for WordChars
  142. '0123456789' //###0.940
  143. + 'abcdefghijklmnopqrstuvwxyz'
  144. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';
  145. RegExprLineSeparators : RegExprString =// default value for LineSeparators
  146. #$d#$a{$IFDEF UniCode}+#$b#$c#$2028#$2029#$85{$ENDIF}; //###0.947
  147. RegExprLinePairedSeparator : RegExprString =// default value for LinePairedSeparator
  148. #$d#$a;
  149. { if You need Unix-styled line separators (only \n), then use:
  150. RegExprLineSeparators = #$a;
  151. RegExprLinePairedSeparator = '';
  152. }
  153. const
  154. NSUBEXP = 90; // max number of subexpression //###0.929
  155. // Cannot be more than NSUBEXPMAX
  156. // Be carefull - don't use values which overflow CLOSE opcode
  157. // (in this case you'll get compiler error).
  158. // Big NSUBEXP will cause more slow work and more stack required
  159. NSUBEXPMAX = 255; // Max possible value for NSUBEXP. //###0.945
  160. // Don't change it! It's defined by internal TRegExpr design.
  161. MaxBracesArg = $7FFFFFFF - 1; // max value for {n,m} arguments //###0.933
  162. {$IFDEF ComplexBraces}
  163. LoopStackMax = 10; // max depth of loops stack //###0.925
  164. {$ENDIF}
  165. TinySetLen = 3;
  166. // if range includes more then TinySetLen chars, //###0.934
  167. // then use full (32 bytes) ANYOFFULL instead of ANYOF[BUT]TINYSET
  168. // !!! Attension ! If you change TinySetLen, you must
  169. // change code marked as "//!!!TinySet"
  170. type
  171. {$IFDEF UseSetOfChar}
  172. PSetOfREChar = ^TSetOfREChar;
  173. TSetOfREChar = set of REChar;
  174. {$ENDIF}
  175. TRegExpr = class;
  176. TRegExprReplaceFunction = function (ARegExpr : TRegExpr): RegExprString of object;
  177. { TRegExpr }
  178. TRegExpr = class
  179. private
  180. startp : array [0 .. NSUBEXP - 1] of PRegExprChar; // founded expr starting points
  181. endp : array [0 .. NSUBEXP - 1] of PRegExprChar; // founded expr end points
  182. {$IFDEF ComplexBraces}
  183. LoopStack : array [1 .. LoopStackMax] of integer; // state before entering loop
  184. LoopStackIdx : integer; // 0 - out of all loops
  185. {$ENDIF}
  186. // The "internal use only" fields to pass info from compile
  187. // to execute that permits the execute phase to run lots faster on
  188. // simple cases.
  189. regstart : REChar; // char that must begin a match; '\0' if none obvious
  190. reganch : REChar; // is the match anchored (at beginning-of-line only)?
  191. regmust : PRegExprChar; // string (pointer into program) that match must include, or nil
  192. regmlen : PtrInt; // length of regmust string
  193. // Regstart and reganch permit very fast decisions on suitable starting points
  194. // for a match, cutting down the work a lot. Regmust permits fast rejection
  195. // of lines that cannot possibly match. The regmust tests are costly enough
  196. // that regcomp() supplies a regmust only if the r.e. contains something
  197. // potentially expensive (at present, the only such thing detected is * or +
  198. // at the start of the r.e., which can involve a lot of backup). Regmlen is
  199. // supplied because the test in regexec() needs it and regcomp() is computing
  200. // it anyway.
  201. {$IFDEF UseFirstCharSet} //###0.929
  202. FirstCharSet : TSetOfREChar;
  203. {$ENDIF}
  204. // work variables for Exec's routins - save stack in recursion}
  205. reginput : PRegExprChar; // String-input pointer.
  206. fInputStart : PRegExprChar; // Pointer to first char of input string.
  207. fInputEnd : PRegExprChar; // Pointer to char AFTER last char of input string
  208. // work variables for compiler's routines
  209. regparse : PRegExprChar; // Input-scan pointer.
  210. regnpar : PtrInt; // count.
  211. regdummy : char;
  212. regcode : PRegExprChar; // Code-emit pointer; @regdummy = don't.
  213. regsize : PtrInt; // Code size.
  214. regexpbeg : PRegExprChar; // only for error handling. Contains
  215. // pointer to beginning of r.e. while compiling
  216. fExprIsCompiled : boolean; // true if r.e. successfully compiled
  217. // programm is essentially a linear encoding
  218. // of a nondeterministic finite-state machine (aka syntax charts or
  219. // "railroad normal form" in parsing technology). Each node is an opcode
  220. // plus a "next" pointer, possibly plus an operand. "Next" pointers of
  221. // all nodes except BRANCH implement concatenation; a "next" pointer with
  222. // a BRANCH on both ends of it connects two alternatives. (Here we
  223. // have one of the subtle syntax dependencies: an individual BRANCH (as
  224. // opposed to a collection of them) is never concatenated with anything
  225. // because of operator precedence.) The operand of some types of node is
  226. // a literal string; for others, it is a node leading into a sub-FSM. In
  227. // particular, the operand of a BRANCH node is the first node of the branch.
  228. // (NB this is *not* a tree structure: the tail of the branch connects
  229. // to the thing following the set of BRANCHes.) The opcodes are:
  230. programm : PRegExprChar; // Unwarranted chumminess with compiler.
  231. fExpression : PRegExprChar; // source of compiled r.e.
  232. fInputString : PRegExprChar; // input string
  233. fLastError : integer; // see Error, LastError
  234. fModifiers : integer; // modifiers
  235. fCompModifiers : integer; // compiler's copy of modifiers
  236. fProgModifiers : integer; // modifiers values from last programm compilation
  237. fSpaceChars : RegExprString; //###0.927
  238. fWordChars : RegExprString; //###0.929
  239. fInvertCase : TRegExprInvertCaseFunction; //###0.927
  240. fLineSeparators : RegExprString; //###0.941
  241. fLinePairedSeparatorAssigned : boolean;
  242. fLinePairedSeparatorHead,
  243. fLinePairedSeparatorTail : REChar;
  244. {$IFNDEF UniCode}
  245. fLineSeparatorsSet : set of REChar;
  246. {$ENDIF}
  247. Function IsSpaceChar(AChar : PRegExprChar) : Boolean; inline;
  248. // Mark programm as having to be [re]compiled
  249. procedure InvalidateProgramm;
  250. // Check if we can use precompiled r.e. or
  251. // [re]compile it if something changed
  252. function IsProgrammOk : boolean; //###0.941
  253. function GetExpression : RegExprString;
  254. procedure SetExpression (const s : RegExprString);
  255. function GetModifierStr : RegExprString;
  256. // Parse AModifiers string and return true and set AModifiersInt
  257. // if it's in format 'ismxrg-ismxrg'.
  258. class function ParseModifiersStr (const AModifiers : RegExprString;
  259. var AModifiersInt : integer) : boolean; //###0.941 class function now
  260. procedure SetModifierStr (const AModifiers : RegExprString);
  261. function GetModifier (AIndex : integer) : boolean;
  262. procedure SetModifier (AIndex : integer; ASet : boolean);
  263. // Default handler raises exception ERegExpr with
  264. // Message = ErrorMsg (AErrorID), ErrorCode = AErrorID
  265. // and CompilerErrorPos = value of property CompilerErrorPos.
  266. procedure Error (AErrorID : integer); virtual; // error handler.
  267. {==================== Compiler section ===================}
  268. // compile a regular expression into internal code
  269. function CompileRegExpr (exp : PRegExprChar) : boolean;
  270. // set the next-pointer at the end of a node chain
  271. procedure Tail (p : PRegExprChar; val : PRegExprChar);
  272. // regoptail - regtail on operand of first argument; nop if operandless
  273. procedure OpTail (p : PRegExprChar; val : PRegExprChar);
  274. // regnode - emit a node, return location
  275. function EmitNode (op : TREOp) : PRegExprChar;
  276. // emit (if appropriate) a byte of code
  277. procedure EmitC (b : REChar);
  278. // insert an operator in front of already-emitted operand
  279. // Means relocating the operand.
  280. procedure InsertOperator (op : TREOp; opnd : PRegExprChar; sz : integer); //###0.90
  281. // regular expression, i.e. main body or parenthesized thing
  282. function ParseReg (paren : integer; var flagp : integer) : PRegExprChar;
  283. // one alternative of an | operator
  284. function ParseBranch (var flagp : integer) : PRegExprChar;
  285. // something followed by possible [*+?]
  286. function ParsePiece (var flagp : integer) : PRegExprChar;
  287. // the lowest level
  288. function ParseAtom (var flagp : integer) : PRegExprChar;
  289. // current pos in r.e. - for error hanling
  290. function GetCompilerErrorPos : PtrInt;
  291. {$IFDEF UseFirstCharSet} //###0.929
  292. procedure FillFirstCharSet (prog : PRegExprChar);
  293. {$ENDIF}
  294. {===================== Matching section ===================}
  295. // repeatedly match something simple, report how many
  296. function regrepeat (p : PRegExprChar; AMax : PtrInt) : PtrInt;
  297. // dig the "next" pointer out of a node
  298. function regnext (p : PRegExprChar) : PRegExprChar;
  299. // recursively matching routine
  300. function MatchPrim (prog : PRegExprChar) : boolean;
  301. // Exec for stored InputString
  302. function ExecPrim (AOffset: PtrInt) : boolean;
  303. {$IFDEF RegExpPCodeDump}
  304. function DumpOp (op : REChar) : RegExprString;
  305. {$ENDIF}
  306. function GetSubExprMatchCount : integer;
  307. function GetMatchPos (Idx : integer) : PtrInt;
  308. function GetMatchLen (Idx : integer) : PtrInt;
  309. function GetMatch (Idx : integer) : RegExprString;
  310. function GetInputString : RegExprString;
  311. procedure SetInputString (const AInputString : RegExprString);
  312. {$IFNDEF UseSetOfChar}
  313. function StrScanCI (s : PRegExprChar; ch : REChar) : PRegExprChar; //###0.928
  314. {$ENDIF}
  315. procedure SetLineSeparators (const AStr : RegExprString);
  316. procedure SetLinePairedSeparator (const AStr : RegExprString);
  317. function GetLinePairedSeparator : RegExprString;
  318. public
  319. constructor Create; overload;
  320. constructor Create(AExpression:string); overload;
  321. destructor Destroy; override;
  322. class function VersionMajor : integer; //###0.944
  323. class function VersionMinor : integer; //###0.944
  324. // Regular expression.
  325. // For optimization, TRegExpr will automatically compiles it into 'P-code'
  326. // (You can see it with help of Dump method) and stores in internal
  327. // structures. Real [re]compilation occures only when it really needed -
  328. // while calling Exec[Next], Substitute, Dump, etc
  329. // and only if Expression or other P-code affected properties was changed
  330. // after last [re]compilation.
  331. // If any errors while [re]compilation occures, Error method is called
  332. // (by default Error raises exception - see below)
  333. property Expression : RegExprString read GetExpression write SetExpression;
  334. // Set/get default values of r.e.syntax modifiers. Modifiers in
  335. // r.e. (?ismx-ismx) will replace this default values.
  336. // If you try to set unsupported modifier, Error will be called
  337. // (by defaul Error raises exception ERegExpr).
  338. property ModifierStr : RegExprString read GetModifierStr write SetModifierStr;
  339. // Modifier /i - caseinsensitive, initialized from RegExprModifierI
  340. property ModifierI : boolean index 1 read GetModifier write SetModifier;
  341. // Modifier /r - use r.e.syntax extended for russian,
  342. // (was property ExtSyntaxEnabled in previous versions)
  343. // If true, then а-я additional include russian letter 'ё',
  344. // А-Я additional include 'Ё', and а-Я include all russian symbols.
  345. // You have to turn it off if it can interfere with you national alphabet.
  346. // , initialized from RegExprModifierR
  347. property ModifierR : boolean index 2 read GetModifier write SetModifier;
  348. // Modifier /s - '.' works as any char (else as [^\n]),
  349. // , initialized from RegExprModifierS
  350. property ModifierS : boolean index 3 read GetModifier write SetModifier;
  351. // Switching off modifier /g switchs all operators in
  352. // non-greedy style, so if ModifierG = False, then
  353. // all '*' works as '*?', all '+' as '+?' and so on.
  354. // , initialized from RegExprModifierG
  355. property ModifierG : boolean index 4 read GetModifier write SetModifier;
  356. // Treat string as multiple lines. That is, change `^' and `$' from
  357. // matching at only the very start or end of the string to the start
  358. // or end of any line anywhere within the string.
  359. // , initialized from RegExprModifierM
  360. property ModifierM : boolean index 5 read GetModifier write SetModifier;
  361. // Modifier /x - eXtended syntax, allow r.e. text formatting,
  362. // see description in the help. Initialized from RegExprModifierX
  363. property ModifierX : boolean index 6 read GetModifier write SetModifier;
  364. // match a programm against a string AInputString
  365. // !!! Exec store AInputString into InputString property
  366. // For Delphi 5 and higher available overloaded versions - first without
  367. // parameter (uses already assigned to InputString property value)
  368. // and second that has PtrInt parameter and is same as ExecPos
  369. function Exec (const AInputString : RegExprString) : boolean; {$IFDEF OverMeth} overload;
  370. {$IFNDEF FPC} // I do not know why FreePascal cannot overload methods with empty param list
  371. function Exec : boolean; overload; //###0.949
  372. {$ENDIF}
  373. function Exec (AOffset: PtrInt) : boolean; overload; //###0.949
  374. {$ENDIF}
  375. // find next match:
  376. // ExecNext;
  377. // works the same as
  378. // if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)
  379. // else ExecPos (MatchPos [0] + MatchLen [0]);
  380. // but it's more simpler !
  381. // Raises exception if used without preceeding SUCCESSFUL call to
  382. // Exec* (Exec, ExecPos, ExecNext). So You always must use something like
  383. // if Exec (InputString) then repeat { proceed results} until not ExecNext;
  384. function ExecNext : boolean;
  385. // find match for InputString starting from AOffset position
  386. // (AOffset=1 - first char of InputString)
  387. function ExecPos (AOffset: PtrInt {$IFDEF DefParam}= 1{$ENDIF}) : boolean;
  388. // returns current input string (from last Exec call or last assign
  389. // to this property).
  390. // Any assignment to this property clear Match* properties !
  391. property InputString : RegExprString read GetInputString write SetInputString;
  392. // Returns ATemplate with '$&' or '$0' replaced by whole r.e.
  393. // occurence and '$n' replaced by occurence of subexpression #n.
  394. // Since v.0.929 '$' used instead of '\' (for future extensions
  395. // and for more Perl-compatibility) and accept more then one digit.
  396. // If you want place into template raw '$' or '\', use prefix '\'
  397. // Example: '1\$ is $2\\rub\\' -> '1$ is <Match[2]>\rub\'
  398. // If you want to place raw digit after '$n' you must delimit
  399. // n with curly braces '{}'.
  400. // Example: 'a$12bc' -> 'a<Match[12]>bc'
  401. // 'a${1}2bc' -> 'a<Match[1]>2bc'.
  402. function Substitute (const ATemplate : RegExprString) : RegExprString;
  403. // Split AInputStr into APieces by r.e. occurencies
  404. // Internally calls Exec[Next]
  405. procedure Split (Const AInputStr : RegExprString; APieces : TStrings);
  406. function Replace (Const AInputStr : RegExprString;
  407. const AReplaceStr : RegExprString;
  408. AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) //###0.946
  409. : RegExprString; {$IFDEF OverMeth} overload;
  410. function Replace (Const AInputStr : RegExprString;
  411. AReplaceFunc : TRegExprReplaceFunction)
  412. : RegExprString; overload;
  413. {$ENDIF}
  414. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr
  415. // If AUseSubstitution is true, then AReplaceStr will be used
  416. // as template for Substitution methods.
  417. // For example:
  418. // Expression := '({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*';
  419. // Replace ('BLOCK( test1)', 'def "$1" value "$2"', True);
  420. // will return: def 'BLOCK' value 'test1'
  421. // Replace ('BLOCK( test1)', 'def "$1" value "$2"')
  422. // will return: def "$1" value "$2"
  423. // Internally calls Exec[Next]
  424. // Overloaded version and ReplaceEx operate with call-back function,
  425. // so you can implement really complex functionality.
  426. function ReplaceEx (Const AInputStr : RegExprString;
  427. AReplaceFunc : TRegExprReplaceFunction):
  428. RegExprString;
  429. // Number of subexpressions has been found in last Exec* call.
  430. // If there are no subexpr. but whole expr was found (Exec* returned True),
  431. // then SubExprMatchCount=0, if no subexpressions nor whole
  432. // r.e. found (Exec* returned false) then SubExprMatchCount=-1.
  433. // Note, that some subexpr. may be not found and for such
  434. // subexpr. MathPos=MatchLen=-1 and Match=''.
  435. // For example: Expression := '(1)?2(3)?';
  436. // Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'
  437. // Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'
  438. // Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'
  439. // Exec ('2'): SubExprMatchCount=0, Match[0]='2'
  440. // Exec ('7') - return False: SubExprMatchCount=-1
  441. property SubExprMatchCount : integer read GetSubExprMatchCount;
  442. // pos of entrance subexpr. #Idx into tested in last Exec*
  443. // string. First subexpr. has Idx=1, last - MatchCount,
  444. // whole r.e. has Idx=0.
  445. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  446. // not found in input string.
  447. property MatchPos [Idx : integer] : PtrInt read GetMatchPos;
  448. // len of entrance subexpr. #Idx r.e. into tested in last Exec*
  449. // string. First subexpr. has Idx=1, last - MatchCount,
  450. // whole r.e. has Idx=0.
  451. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  452. // not found in input string.
  453. // Remember - MatchLen may be 0 (if r.e. match empty string) !
  454. property MatchLen [Idx : integer] : PtrInt read GetMatchLen;
  455. // == copy (InputString, MatchPos [Idx], MatchLen [Idx])
  456. // Returns '' if in r.e. no such subexpr. or this subexpr.
  457. // not found in input string.
  458. property Match [Idx : integer] : RegExprString read GetMatch;
  459. // Returns ID of last error, 0 if no errors (unusable if
  460. // Error method raises exception) and clear internal status
  461. // into 0 (no errors).
  462. function LastError : integer;
  463. // Returns Error message for error with ID = AErrorID.
  464. function ErrorMsg (AErrorID : integer) : RegExprString; virtual;
  465. // Returns position in r.e. where compiler stopped.
  466. // Useful for error diagnostics
  467. property CompilerErrorPos : PtrInt read GetCompilerErrorPos;
  468. // Contains chars, treated as /s (initially filled with RegExprSpaceChars
  469. // global constant)
  470. property SpaceChars : RegExprString read fSpaceChars write fSpaceChars; //###0.927
  471. // Contains chars, treated as /w (initially filled with RegExprWordChars
  472. // global constant)
  473. property WordChars : RegExprString read fWordChars write fWordChars; //###0.929
  474. // line separators (like \n in Unix)
  475. property LineSeparators : RegExprString read fLineSeparators write SetLineSeparators; //###0.941
  476. // paired line separator (like \r\n in DOS and Windows).
  477. // must contain exactly two chars or no chars at all
  478. property LinePairedSeparator : RegExprString read GetLinePairedSeparator write SetLinePairedSeparator; //###0.941
  479. // Converts Ch into upper case if it in lower case or in lower
  480. // if it in upper (uses current system local setings)
  481. class function InvertCaseFunction (const Ch : REChar) : REChar;
  482. // Set this property if you want to override case-insensitive functionality.
  483. // Create set it to RegExprInvertCaseFunction (InvertCaseFunction by default)
  484. property InvertCase : TRegExprInvertCaseFunction read fInvertCase write fInvertCase; //##0.935
  485. // [Re]compile r.e. Useful for example for GUI r.e. editors (to check
  486. // all properties validity).
  487. procedure Compile; //###0.941
  488. {$IFDEF RegExpPCodeDump}
  489. // dump a compiled regexp in vaguely comprehensible form
  490. function Dump : RegExprString;
  491. {$ENDIF}
  492. end;
  493. ERegExpr = class (Exception)
  494. public
  495. ErrorCode : integer;
  496. CompilerErrorPos : PtrInt;
  497. end;
  498. const
  499. // default for InvertCase property:
  500. RegExprInvertCaseFunction : TRegExprInvertCaseFunction = {$IFDEF FPC} nil {$ELSE} TRegExpr.InvertCaseFunction{$ENDIF};
  501. // true if string AInputString match regular expression ARegExpr
  502. // ! will raise exeption if syntax errors in ARegExpr
  503. function ExecRegExpr (const ARegExpr, AInputStr : RegExprString) : boolean;
  504. // Split AInputStr into APieces by r.e. ARegExpr occurencies
  505. procedure SplitRegExpr (const ARegExpr, AInputStr : RegExprString; APieces : TStrings);
  506. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr
  507. // If AUseSubstitution is true, then AReplaceStr will be used
  508. // as template for Substitution methods.
  509. // For example:
  510. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  511. // 'BLOCK( test1)', 'def "$1" value "$2"', True)
  512. // will return: def 'BLOCK' value 'test1'
  513. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  514. // 'BLOCK( test1)', 'def "$1" value "$2"')
  515. // will return: def "$1" value "$2"
  516. function ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : RegExprString;
  517. AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString; //###0.947
  518. // Replace all metachars with its safe representation,
  519. // for example 'abc$cd.(' converts into 'abc\$cd\.\('
  520. // This function useful for r.e. autogeneration from
  521. // user input
  522. function QuoteRegExprMetaChars (const AStr : RegExprString) : RegExprString;
  523. // Makes list of subexpressions found in ARegExpr r.e.
  524. // In ASubExps every item represent subexpression,
  525. // from first to last, in format:
  526. // String - subexpression text (without '()')
  527. // low word of Object - starting position in ARegExpr, including '('
  528. // if exists! (first position is 1)
  529. // high word of Object - length, including starting '(' and ending ')'
  530. // if exist!
  531. // AExtendedSyntax - must be True if modifier /m will be On while
  532. // using the r.e.
  533. // Useful for GUI editors of r.e. etc (You can find example of using
  534. // in TestRExp.dpr project)
  535. // Returns
  536. // 0 Success. No unbalanced brackets was found;
  537. // -1 There are not enough closing brackets ')';
  538. // -(n+1) At position n was found opening '[' without //###0.942
  539. // corresponding closing ']';
  540. // n At position n was found closing bracket ')' without
  541. // corresponding opening '('.
  542. // If Result <> 0, then ASubExpr can contain empty items or illegal ones
  543. function RegExprSubExpressions (const ARegExpr : string;
  544. ASubExprs : TStrings; AExtendedSyntax : boolean{$IFDEF DefParam}= False{$ENDIF}) : PtrInt;
  545. implementation
  546. {$IFDEF FPC}
  547. {$ELSE}
  548. uses
  549. {$IFDEF SYN_WIN32}
  550. Windows; // CharUpper/Lower
  551. {$ELSE}
  552. Libc; //Qt.pas from Borland does not expose char handling functions
  553. {$ENDIF}
  554. {$ENDIF}
  555. const
  556. // TRegExpr.VersionMajor/Minor return values of these constants:
  557. TRegExprVersionMajor : integer = 0;
  558. TRegExprVersionMinor : integer = 952;
  559. MaskModI = 1; // modifier /i bit in fModifiers
  560. MaskModR = 2; // -"- /r
  561. MaskModS = 4; // -"- /s
  562. MaskModG = 8; // -"- /g
  563. MaskModM = 16; // -"- /m
  564. MaskModX = 32; // -"- /x
  565. {$IFDEF UniCode}
  566. XIgnoredChars = ' '#9#$d#$a;
  567. {$ELSE}
  568. XIgnoredChars = [' ', #9, #$d, #$a];
  569. {$ENDIF}
  570. function AlignToPtr(const p: Pointer): Pointer;
  571. begin
  572. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  573. Result := Align(p, SizeOf(Pointer));
  574. {$ELSE}
  575. Result := p;
  576. {$ENDIF}
  577. end;
  578. function AlignToInt(const p: Pointer): Pointer;
  579. begin
  580. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  581. Result := Align(p, SizeOf(integer));
  582. {$ELSE}
  583. Result := p;
  584. {$ENDIF}
  585. end;
  586. {=============================================================}
  587. {=================== WideString functions ====================}
  588. {=============================================================}
  589. {$IFDEF UniCode}
  590. function StrPCopy (Dest: PRegExprChar; const Source: RegExprString): PRegExprChar;
  591. var
  592. Len : PtrInt;
  593. begin
  594. Len := length (Source); //###0.932
  595. if Len>0 then
  596. move(Source[1],Dest[0],Len*sizeof(ReChar));
  597. Dest [Len] := #0;
  598. Result := Dest;
  599. end; { of function StrPCopy
  600. --------------------------------------------------------------}
  601. function StrLCopy (Dest, Source: PRegExprChar; MaxLen: PtrUInt): PRegExprChar;
  602. begin
  603. if MaxLen>0 then
  604. move(Source[0],Dest[0],MaxLen*sizeof(ReChar));
  605. Result := Dest;
  606. end; { of function StrLCopy
  607. --------------------------------------------------------------}
  608. function StrLen (Str: PRegExprChar): PtrUInt;
  609. begin
  610. Result:=0;
  611. while Str [result] <> #0 do
  612. Inc (Result);
  613. end; { of function StrLen
  614. --------------------------------------------------------------}
  615. function StrPos (Str1, Str2: PRegExprChar): PRegExprChar;
  616. var
  617. n: PtrInt;
  618. begin
  619. Result := nil;
  620. n := Pos (RegExprString (Str2), RegExprString (Str1));
  621. if n = 0 then
  622. EXIT;
  623. Result := Str1 + n - 1;
  624. end; { of function StrPos
  625. --------------------------------------------------------------}
  626. function StrLComp (Str1, Str2: PRegExprChar; MaxLen: PtrUInt): PtrInt;
  627. var
  628. S1, S2: RegExprString;
  629. begin
  630. S1 := Str1;
  631. S2 := Str2;
  632. if Length(S1) > MaxLen then
  633. SetLength(S1, MaxLen);
  634. if Length(S2) > MaxLen then
  635. SetLength(S2, MaxLen);
  636. if S1 > S2 then
  637. Result := 1
  638. else if S2 < S2 then
  639. Result := -1
  640. else
  641. Result := 0;
  642. end; { function StrLComp
  643. --------------------------------------------------------------}
  644. function StrScan (Str: PRegExprChar; Chr: WideChar): PRegExprChar;
  645. begin
  646. Result := nil;
  647. while (Str^ <> #0) and (Str^ <> Chr) do
  648. Inc (Str);
  649. if (Str^ <> #0) then
  650. Result := Str;
  651. end; { of function StrScan
  652. --------------------------------------------------------------}
  653. {$ENDIF}
  654. {=============================================================}
  655. {===================== Global functions ======================}
  656. {=============================================================}
  657. function ExecRegExpr (const ARegExpr, AInputStr : RegExprString) : boolean;
  658. begin
  659. With TRegExpr.Create do
  660. try
  661. Expression := ARegExpr;
  662. Result := Exec (AInputStr);
  663. finally
  664. Free;
  665. end;
  666. end; { of function ExecRegExpr
  667. --------------------------------------------------------------}
  668. procedure SplitRegExpr (const ARegExpr, AInputStr : RegExprString; APieces : TStrings);
  669. begin
  670. APieces.Clear;
  671. With TRegExpr.Create do
  672. try
  673. Expression := ARegExpr;
  674. Split (AInputStr, APieces);
  675. finally
  676. Free;
  677. end;
  678. end; { of procedure SplitRegExpr
  679. --------------------------------------------------------------}
  680. function ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : RegExprString;
  681. AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString;
  682. begin
  683. with TRegExpr.Create do
  684. try
  685. Expression := ARegExpr;
  686. Result := Replace (AInputStr, AReplaceStr, AUseSubstitution);
  687. finally
  688. Free;
  689. end;
  690. end; { of function ReplaceRegExpr
  691. --------------------------------------------------------------}
  692. function QuoteRegExprMetaChars (const AStr : RegExprString) : RegExprString;
  693. const
  694. RegExprMetaSet : RegExprString = '^$.[()|?+*'+EscChar+'{'
  695. + ']}'; // - this last are additional to META.
  696. // Very similar to META array, but slighly changed.
  697. // !Any changes in META array must be synchronized with this set.
  698. var
  699. i, i0, Len : PtrInt;
  700. begin
  701. Result := '';
  702. Len := length (AStr);
  703. i := 1;
  704. i0 := i;
  705. while i <= Len do begin
  706. if Pos (AStr [i], RegExprMetaSet) > 0 then begin
  707. Result := Result + System.Copy (AStr, i0, i - i0)
  708. + EscChar + AStr [i];
  709. i0 := i + 1;
  710. end;
  711. inc (i);
  712. end;
  713. Result := Result + System.Copy (AStr, i0, MaxInt); // Tail
  714. end; { of function QuoteRegExprMetaChars
  715. --------------------------------------------------------------}
  716. function RegExprSubExpressions (const ARegExpr : string;
  717. ASubExprs : TStrings; AExtendedSyntax : boolean{$IFDEF DefParam}= False{$ENDIF}) : PtrInt;
  718. type
  719. TStackItemRec = record //###0.945
  720. SubExprIdx : integer;
  721. StartPos : PtrInt;
  722. end;
  723. TStackArray = packed array [0 .. NSUBEXPMAX - 1] of TStackItemRec;
  724. var
  725. Len, SubExprLen : PtrInt;
  726. i, i0 : PtrInt;
  727. Modif : integer;
  728. Stack : ^TStackArray; //###0.945
  729. StackIdx, StackSz : PtrInt;
  730. begin
  731. Result := 0; // no unbalanced brackets found at this very moment
  732. Modif:=0;
  733. ASubExprs.Clear; // I don't think that adding to non empty list
  734. // can be useful, so I simplified algorithm to work only with empty list
  735. Len := length (ARegExpr); // some optimization tricks
  736. // first we have to calculate number of subexpression to reserve
  737. // space in Stack array (may be we'll reserve more than needed, but
  738. // it's faster then memory reallocation during parsing)
  739. StackSz := 1; // add 1 for entire r.e.
  740. for i := 1 to Len do
  741. if ARegExpr [i] = '('
  742. then inc (StackSz);
  743. // SetLength (Stack, StackSz); //###0.945
  744. GetMem (Stack, SizeOf (TStackItemRec) * StackSz);
  745. try
  746. StackIdx := 0;
  747. i := 1;
  748. while (i <= Len) do begin
  749. case ARegExpr [i] of
  750. '(': begin
  751. if (i < Len) and (ARegExpr [i + 1] = '?') then begin
  752. // this is not subexpression, but comment or other
  753. // Perl extension. We must check is it (?ismxrg-ismxrg)
  754. // and change AExtendedSyntax if /x is changed.
  755. inc (i, 2); // skip '(?'
  756. i0 := i;
  757. while (i <= Len) and (ARegExpr [i] <> ')')
  758. do inc (i);
  759. if i > Len
  760. then Result := -1 // unbalansed '('
  761. else
  762. if TRegExpr.ParseModifiersStr (System.Copy (ARegExpr, i, i - i0), Modif)
  763. then AExtendedSyntax := (Modif and MaskModX) <> 0;
  764. end
  765. else begin // subexpression starts
  766. ASubExprs.Add (''); // just reserve space
  767. with Stack [StackIdx] do begin
  768. SubExprIdx := ASubExprs.Count - 1;
  769. StartPos := i;
  770. end;
  771. inc (StackIdx);
  772. end;
  773. end;
  774. ')': begin
  775. if StackIdx = 0
  776. then Result := i // unbalanced ')'
  777. else begin
  778. dec (StackIdx);
  779. with Stack [StackIdx] do begin
  780. SubExprLen := i - StartPos + 1;
  781. ASubExprs.Objects [SubExprIdx] :=
  782. TObject (StartPos or (SubExprLen ShL 16));
  783. ASubExprs [SubExprIdx] := System.Copy (
  784. ARegExpr, StartPos + 1, SubExprLen - 2); // add without brackets
  785. end;
  786. end;
  787. end;
  788. EscChar: inc (i); // skip quoted symbol
  789. '[': begin
  790. // we have to skip character ranges at once, because they can
  791. // contain '#', and '#' in it must NOT be recognized as eXtended
  792. // comment beginning!
  793. i0 := i;
  794. inc (i);
  795. if ARegExpr [i] = ']' // cannot be 'emty' ranges - this interpretes
  796. then inc (i); // as ']' by itself
  797. while (i <= Len) and (ARegExpr [i] <> ']') do
  798. if ARegExpr [i] = EscChar //###0.942
  799. then inc (i, 2) // skip 'escaped' char to prevent stopping at '\]'
  800. else inc (i);
  801. if (i > Len) or (ARegExpr [i] <> ']') //###0.942
  802. then Result := - (i0 + 1); // unbalansed '[' //###0.942
  803. end;
  804. '#': if AExtendedSyntax then begin
  805. // skip eXtended comments
  806. while (i <= Len) and (ARegExpr [i] <> #$d) and (ARegExpr [i] <> #$a)
  807. // do not use [#$d, #$a] due to UniCode compatibility
  808. do inc (i);
  809. while (i + 1 <= Len) and ((ARegExpr [i + 1] = #$d) or (ARegExpr [i + 1] = #$a))
  810. do inc (i); // attempt to work with different kinds of line separators
  811. // now we are at the line separator that must be skipped.
  812. end;
  813. // here is no 'else' clause - we simply skip ordinary chars
  814. end; // of case
  815. inc (i); // skip scanned char
  816. // ! can move after Len due to skipping quoted symbol
  817. end;
  818. // check brackets balance
  819. if StackIdx <> 0
  820. then Result := -1; // unbalansed '('
  821. // check if entire r.e. added
  822. if (ASubExprs.Count = 0)
  823. or ((PtrInt (ASubExprs.Objects [0]) and $FFFF) <> 1)
  824. or (((PtrInt (ASubExprs.Objects [0]) ShR 16) and $FFFF) <> Len)
  825. // whole r.e. wasn't added because it isn't bracketed
  826. // well, we add it now:
  827. then ASubExprs.InsertObject (0, ARegExpr, TObject ((Len ShL 16) or 1));
  828. finally FreeMem (Stack);
  829. end;
  830. end; { of function RegExprSubExpressions
  831. --------------------------------------------------------------}
  832. const
  833. MAGIC = TREOp (216);// programm signature
  834. // name opcode opnd? meaning
  835. EEND = TREOp (0); // - End of program
  836. BOL = TREOp (1); // - Match "" at beginning of line
  837. EOL = TREOp (2); // - Match "" at end of line
  838. ANY = TREOp (3); // - Match any one character
  839. ANYOF = TREOp (4); // Str Match any character in string Str
  840. ANYBUT = TREOp (5); // Str Match any char. not in string Str
  841. BRANCH = TREOp (6); // Node Match this alternative, or the next
  842. BACK = TREOp (7); // - Jump backward (Next < 0)
  843. EXACTLY = TREOp (8); // Str Match string Str
  844. NOTHING = TREOp (9); // - Match empty string
  845. STAR = TREOp (10); // Node Match this (simple) thing 0 or more times
  846. PLUS = TREOp (11); // Node Match this (simple) thing 1 or more times
  847. ANYDIGIT = TREOp (12); // - Match any digit (equiv [0-9])
  848. NOTDIGIT = TREOp (13); // - Match not digit (equiv [0-9])
  849. ANYLETTER = TREOp (14); // - Match any letter from property WordChars
  850. NOTLETTER = TREOp (15); // - Match not letter from property WordChars
  851. ANYSPACE = TREOp (16); // - Match any space char (see property SpaceChars)
  852. NOTSPACE = TREOp (17); // - Match not space char (see property SpaceChars)
  853. BRACES = TREOp (18); // Node,Min,Max Match this (simple) thing from Min to Max times.
  854. // Min and Max are TREBracesArg
  855. COMMENT = TREOp (19); // - Comment ;)
  856. EXACTLYCI = TREOp (20); // Str Match string Str case insensitive
  857. ANYOFCI = TREOp (21); // Str Match any character in string Str, case insensitive
  858. ANYBUTCI = TREOp (22); // Str Match any char. not in string Str, case insensitive
  859. LOOPENTRY = TREOp (23); // Node Start of loop (Node - LOOP for this loop)
  860. LOOP = TREOp (24); // Node,Min,Max,LoopEntryJmp - back jump for LOOPENTRY.
  861. // Min and Max are TREBracesArg
  862. // Node - next node in sequence,
  863. // LoopEntryJmp - associated LOOPENTRY node addr
  864. ANYOFTINYSET= TREOp (25); // Chrs Match any one char from Chrs (exactly TinySetLen chars)
  865. ANYBUTTINYSET=TREOp (26); // Chrs Match any one char not in Chrs (exactly TinySetLen chars)
  866. ANYOFFULLSET= TREOp (27); // Set Match any one char from set of char
  867. // - very fast (one CPU instruction !) but takes 32 bytes of p-code
  868. BSUBEXP = TREOp (28); // Idx Match previously matched subexpression #Idx (stored as REChar) //###0.936
  869. BSUBEXPCI = TREOp (29); // Idx -"- in case-insensitive mode
  870. // Non-Greedy Style Ops //###0.940
  871. STARNG = TREOp (30); // Same as START but in non-greedy mode
  872. PLUSNG = TREOp (31); // Same as PLUS but in non-greedy mode
  873. BRACESNG = TREOp (32); // Same as BRACES but in non-greedy mode
  874. LOOPNG = TREOp (33); // Same as LOOP but in non-greedy mode
  875. // Multiline mode \m
  876. BOLML = TREOp (34); // - Match "" at beginning of line
  877. EOLML = TREOp (35); // - Match "" at end of line
  878. ANYML = TREOp (36); // - Match any one character
  879. // Word boundary
  880. BOUND = TREOp (37); // Match "" between words //###0.943
  881. NOTBOUND = TREOp (38); // Match "" not between words //###0.943
  882. // !!! Change OPEN value if you add new opcodes !!!
  883. OPEN = TREOp (39); // - Mark this point in input as start of \n
  884. // OPEN + 1 is \1, etc.
  885. CLOSE = TREOp (ord (OPEN) + NSUBEXP);
  886. // - Analogous to OPEN.
  887. // !!! Don't add new OpCodes after CLOSE !!!
  888. // We work with p-code through pointers, compatible with PRegExprChar.
  889. // Note: all code components (TRENextOff, TREOp, TREBracesArg, etc)
  890. // must have lengths that can be divided by SizeOf (REChar) !
  891. // A node is TREOp of opcode followed Next "pointer" of TRENextOff type.
  892. // The Next is a offset from the opcode of the node containing it.
  893. // An operand, if any, simply follows the node. (Note that much of
  894. // the code generation knows about this implicit relationship!)
  895. // Using TRENextOff=PtrInt speed up p-code processing.
  896. // Opcodes description:
  897. //
  898. // BRANCH The set of branches constituting a single choice are hooked
  899. // together with their "next" pointers, since precedence prevents
  900. // anything being concatenated to any individual branch. The
  901. // "next" pointer of the last BRANCH in a choice points to the
  902. // thing following the whole choice. This is also where the
  903. // final "next" pointer of each individual branch points; each
  904. // branch starts with the operand node of a BRANCH node.
  905. // BACK Normal "next" pointers all implicitly point forward; BACK
  906. // exists to make loop structures possible.
  907. // STAR,PLUS,BRACES '?', and complex '*' and '+', are implemented as
  908. // circular BRANCH structures using BACK. Complex '{min,max}'
  909. // - as pair LOOPENTRY-LOOP (see below). Simple cases (one
  910. // character per match) are implemented with STAR, PLUS and
  911. // BRACES for speed and to minimize recursive plunges.
  912. // LOOPENTRY,LOOP {min,max} are implemented as special pair
  913. // LOOPENTRY-LOOP. Each LOOPENTRY initialize loopstack for
  914. // current level.
  915. // OPEN,CLOSE are numbered at compile time.
  916. {=============================================================}
  917. {================== Error handling section ===================}
  918. {=============================================================}
  919. const
  920. reeOk = 0;
  921. reeCompNullArgument = 100;
  922. reeCompRegexpTooBig = 101;
  923. reeCompParseRegTooManyBrackets = 102;
  924. reeCompParseRegUnmatchedBrackets = 103;
  925. reeCompParseRegUnmatchedBrackets2 = 104;
  926. reeCompParseRegJunkOnEnd = 105;
  927. reePlusStarOperandCouldBeEmpty = 106;
  928. reeNestedSQP = 107;
  929. reeBadHexDigit = 108;
  930. reeInvalidRange = 109;
  931. reeParseAtomTrailingBackSlash = 110;
  932. reeNoHexCodeAfterBSlashX = 111;
  933. reeHexCodeAfterBSlashXTooBig = 112;
  934. reeUnmatchedSqBrackets = 113;
  935. reeInternalUrp = 114;
  936. reeQPSBFollowsNothing = 115;
  937. reeTrailingBackSlash = 116;
  938. reeRarseAtomInternalDisaster = 119;
  939. reeBRACESArgTooBig = 122;
  940. reeBracesMinParamGreaterMax = 124;
  941. reeUnclosedComment = 125;
  942. reeComplexBracesNotImplemented = 126;
  943. reeUrecognizedModifier = 127;
  944. reeBadLinePairedSeparator = 128;
  945. reeRegRepeatCalledInappropriately = 1000;
  946. reeMatchPrimMemoryCorruption = 1001;
  947. reeMatchPrimCorruptedPointers = 1002;
  948. reeNoExpression = 1003;
  949. reeCorruptedProgram = 1004;
  950. reeNoInputStringSpecified = 1005;
  951. reeOffsetMustBeGreaterThen0 = 1006;
  952. reeExecNextWithoutExec = 1007;
  953. reeGetInputStringWithoutInputString = 1008;
  954. reeDumpCorruptedOpcode = 1011;
  955. reeModifierUnsupported = 1013;
  956. reeLoopStackExceeded = 1014;
  957. reeLoopWithoutEntry = 1015;
  958. reeBadPCodeImported = 2000;
  959. function TRegExpr.ErrorMsg (AErrorID : integer) : RegExprString;
  960. begin
  961. case AErrorID of
  962. reeOk: Result := 'No errors';
  963. reeCompNullArgument: Result := 'TRegExpr(comp): Null Argument';
  964. reeCompRegexpTooBig: Result := 'TRegExpr(comp): Regexp Too Big';
  965. reeCompParseRegTooManyBrackets: Result := 'TRegExpr(comp): ParseReg Too Many ()';
  966. reeCompParseRegUnmatchedBrackets: Result := 'TRegExpr(comp): ParseReg Unmatched ()';
  967. reeCompParseRegUnmatchedBrackets2: Result := 'TRegExpr(comp): ParseReg Unmatched ()';
  968. reeCompParseRegJunkOnEnd: Result := 'TRegExpr(comp): ParseReg Junk On End';
  969. reePlusStarOperandCouldBeEmpty: Result := 'TRegExpr(comp): *+ Operand Could Be Empty';
  970. reeNestedSQP: Result := 'TRegExpr(comp): Nested *?+';
  971. reeBadHexDigit: Result := 'TRegExpr(comp): Bad Hex Digit';
  972. reeInvalidRange: Result := 'TRegExpr(comp): Invalid [] Range';
  973. reeParseAtomTrailingBackSlash: Result := 'TRegExpr(comp): Parse Atom Trailing \';
  974. reeNoHexCodeAfterBSlashX: Result := 'TRegExpr(comp): No Hex Code After \x';
  975. reeHexCodeAfterBSlashXTooBig: Result := 'TRegExpr(comp): Hex Code After \x Is Too Big';
  976. reeUnmatchedSqBrackets: Result := 'TRegExpr(comp): Unmatched []';
  977. reeInternalUrp: Result := 'TRegExpr(comp): Internal Urp';
  978. reeQPSBFollowsNothing: Result := 'TRegExpr(comp): ?+*{ Follows Nothing';
  979. reeTrailingBackSlash: Result := 'TRegExpr(comp): Trailing \';
  980. reeRarseAtomInternalDisaster: Result := 'TRegExpr(comp): RarseAtom Internal Disaster';
  981. reeBRACESArgTooBig: Result := 'TRegExpr(comp): BRACES Argument Too Big';
  982. reeBracesMinParamGreaterMax: Result := 'TRegExpr(comp): BRACE Min Param Greater then Max';
  983. reeUnclosedComment: Result := 'TRegExpr(comp): Unclosed (?#Comment)';
  984. reeComplexBracesNotImplemented: Result := 'TRegExpr(comp): If you want take part in beta-testing BRACES ''{min,max}'' and non-greedy ops ''*?'', ''+?'', ''??'' for complex cases - remove ''.'' from {.$DEFINE ComplexBraces}';
  985. reeUrecognizedModifier: Result := 'TRegExpr(comp): Urecognized Modifier';
  986. reeBadLinePairedSeparator: Result := 'TRegExpr(comp): LinePairedSeparator must countain two different chars or no chars at all';
  987. reeRegRepeatCalledInappropriately: Result := 'TRegExpr(exec): RegRepeat Called Inappropriately';
  988. reeMatchPrimMemoryCorruption: Result := 'TRegExpr(exec): MatchPrim Memory Corruption';
  989. reeMatchPrimCorruptedPointers: Result := 'TRegExpr(exec): MatchPrim Corrupted Pointers';
  990. reeNoExpression: Result := 'TRegExpr(exec): Not Assigned Expression Property';
  991. reeCorruptedProgram: Result := 'TRegExpr(exec): Corrupted Program';
  992. reeNoInputStringSpecified: Result := 'TRegExpr(exec): No Input String Specified';
  993. reeOffsetMustBeGreaterThen0: Result := 'TRegExpr(exec): Offset Must Be Greater Then 0';
  994. reeExecNextWithoutExec: Result := 'TRegExpr(exec): ExecNext Without Exec[Pos]';
  995. reeGetInputStringWithoutInputString: Result := 'TRegExpr(exec): GetInputString Without InputString';
  996. reeDumpCorruptedOpcode: Result := 'TRegExpr(dump): Corrupted Opcode';
  997. reeLoopStackExceeded: Result := 'TRegExpr(exec): Loop Stack Exceeded';
  998. reeLoopWithoutEntry: Result := 'TRegExpr(exec): Loop Without LoopEntry !';
  999. reeBadPCodeImported: Result := 'TRegExpr(misc): Bad p-code imported';
  1000. else Result := 'Unknown error';
  1001. end;
  1002. end; { of procedure TRegExpr.Error
  1003. --------------------------------------------------------------}
  1004. function TRegExpr.LastError : integer;
  1005. begin
  1006. Result := fLastError;
  1007. fLastError := reeOk;
  1008. end; { of function TRegExpr.LastError
  1009. --------------------------------------------------------------}
  1010. {=============================================================}
  1011. {===================== Common section ========================}
  1012. {=============================================================}
  1013. class function TRegExpr.VersionMajor : integer; //###0.944
  1014. begin
  1015. Result := TRegExprVersionMajor;
  1016. end; { of class function TRegExpr.VersionMajor
  1017. --------------------------------------------------------------}
  1018. class function TRegExpr.VersionMinor : integer; //###0.944
  1019. begin
  1020. Result := TRegExprVersionMinor;
  1021. end; { of class function TRegExpr.VersionMinor
  1022. --------------------------------------------------------------}
  1023. constructor TRegExpr.Create;
  1024. begin
  1025. inherited;
  1026. programm := nil;
  1027. fExpression := nil;
  1028. fInputString := nil;
  1029. regexpbeg := nil;
  1030. fExprIsCompiled := false;
  1031. ModifierI := RegExprModifierI;
  1032. ModifierR := RegExprModifierR;
  1033. ModifierS := RegExprModifierS;
  1034. ModifierG := RegExprModifierG;
  1035. ModifierM := RegExprModifierM; //###0.940
  1036. SpaceChars := RegExprSpaceChars; //###0.927
  1037. WordChars := RegExprWordChars; //###0.929
  1038. fInvertCase := RegExprInvertCaseFunction; //###0.927
  1039. fLineSeparators := RegExprLineSeparators; //###0.941
  1040. LinePairedSeparator := RegExprLinePairedSeparator; //###0.941
  1041. end; { of constructor TRegExpr.Create
  1042. --------------------------------------------------------------}
  1043. constructor TRegExpr.Create(AExpression:string);
  1044. begin
  1045. create;
  1046. Expression:=AExpression;
  1047. end;
  1048. destructor TRegExpr.Destroy;
  1049. begin
  1050. if programm <> nil then
  1051. begin
  1052. FreeMem (programm);
  1053. programm:=nil;
  1054. end;
  1055. if fExpression <> nil then
  1056. begin
  1057. FreeMem (fExpression);
  1058. fExpression:=nil;
  1059. end;
  1060. if fInputString <> nil then
  1061. begin
  1062. FreeMem (fInputString);
  1063. fInputString:=nil;
  1064. end;
  1065. end; { of destructor TRegExpr.Destroy
  1066. --------------------------------------------------------------}
  1067. {$IFDEF FPC}
  1068. {$IFDEF UNICODE}
  1069. function AnsiUpperCase(const s: RegExprString): RegExprString;inline;
  1070. begin
  1071. Result:=WideUpperCase(S);
  1072. end;
  1073. function AnsiLowerCase(const s: RegExprString): RegExprString;inline;
  1074. begin
  1075. Result:=WideLowerCase(S);
  1076. end;
  1077. {$ENDIF}
  1078. {$ENDIF}
  1079. class function TRegExpr.InvertCaseFunction (const Ch : REChar) : REChar;
  1080. begin
  1081. {$IFDEF FPC}
  1082. Result := AnsiUpperCase(Ch)[1];
  1083. if Result = Ch then
  1084. Result := AnsiLowerCase(Ch)[1];
  1085. {$ELSE}
  1086. {$IFDEF SYN_WIN32}
  1087. Result := REChar (CharUpper (PChar (Ch)));
  1088. if Result = Ch then
  1089. Result := REChar (CharLower (PChar (Ch)));
  1090. {$ELSE}
  1091. Result := REChar (toupper (integer (Ch)));
  1092. if Result = Ch then
  1093. Result := REChar(tolower (integer (Ch)));
  1094. {$ENDIF}
  1095. {$ENDIF}
  1096. end; { of function TRegExpr.InvertCaseFunction
  1097. --------------------------------------------------------------}
  1098. function TRegExpr.GetExpression : RegExprString;
  1099. begin
  1100. if fExpression <> nil
  1101. then Result := fExpression
  1102. else Result := '';
  1103. end; { of function TRegExpr.GetExpression
  1104. --------------------------------------------------------------}
  1105. procedure TRegExpr.SetExpression (const s : RegExprString);
  1106. var
  1107. Len : PtrInt; //###0.950
  1108. begin
  1109. if (s <> fExpression) or not fExprIsCompiled then begin
  1110. fExprIsCompiled := false;
  1111. if fExpression <> nil then begin
  1112. FreeMem (fExpression);
  1113. fExpression := nil;
  1114. end;
  1115. if s <> '' then begin
  1116. Len := length (s); //###0.950
  1117. GetMem (fExpression, (Len + 1) * SizeOf (REChar));
  1118. System.Move(s[1],fExpression^,(Len + 1) * SizeOf (REChar));
  1119. InvalidateProgramm; //###0.941
  1120. end;
  1121. end;
  1122. end; { of procedure TRegExpr.SetExpression
  1123. --------------------------------------------------------------}
  1124. function TRegExpr.GetSubExprMatchCount : integer;
  1125. begin
  1126. if Assigned (fInputString) then begin
  1127. Result := NSUBEXP - 1;
  1128. while (Result > 0) and ((startp [Result] = nil)
  1129. or (endp [Result] = nil))
  1130. do dec (Result);
  1131. end
  1132. else Result := -1;
  1133. end; { of function TRegExpr.GetSubExprMatchCount
  1134. --------------------------------------------------------------}
  1135. function TRegExpr.GetMatchPos (Idx : integer) : PtrInt;
  1136. begin
  1137. if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)
  1138. and Assigned (startp [Idx]) and Assigned (endp [Idx]) then begin
  1139. Result := (startp [Idx] - fInputString) + 1;
  1140. end
  1141. else Result := -1;
  1142. end; { of function TRegExpr.GetMatchPos
  1143. --------------------------------------------------------------}
  1144. function TRegExpr.GetMatchLen (Idx : integer) : PtrInt;
  1145. begin
  1146. if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)
  1147. and Assigned (startp [Idx]) and Assigned (endp [Idx]) then begin
  1148. Result := endp [Idx] - startp [Idx];
  1149. end
  1150. else Result := -1;
  1151. end; { of function TRegExpr.GetMatchLen
  1152. --------------------------------------------------------------}
  1153. function TRegExpr.GetMatch (Idx : integer) : RegExprString;
  1154. begin
  1155. Result:='';
  1156. if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)
  1157. and Assigned (startp [Idx]) and Assigned (endp [Idx])
  1158. and (endp [Idx] > startp[Idx])
  1159. //then Result := copy (fInputString, MatchPos [Idx], MatchLen [Idx]) //###0.929
  1160. then begin
  1161. //SetString (Result, startp [idx], endp [idx] - startp [idx])
  1162. SetLength(Result,endp [idx] - startp [idx]);
  1163. System.Move(startp [idx]^,Result[1],length(Result)*sizeof(REChar));
  1164. end
  1165. else Result := '';
  1166. end; { of function TRegExpr.GetMatch
  1167. --------------------------------------------------------------}
  1168. function TRegExpr.GetModifierStr : RegExprString;
  1169. begin
  1170. Result := '-';
  1171. if ModifierI
  1172. then Result := 'i' + Result
  1173. else Result := Result + 'i';
  1174. if ModifierR
  1175. then Result := 'r' + Result
  1176. else Result := Result + 'r';
  1177. if ModifierS
  1178. then Result := 's' + Result
  1179. else Result := Result + 's';
  1180. if ModifierG
  1181. then Result := 'g' + Result
  1182. else Result := Result + 'g';
  1183. if ModifierM
  1184. then Result := 'm' + Result
  1185. else Result := Result + 'm';
  1186. if ModifierX
  1187. then Result := 'x' + Result
  1188. else Result := Result + 'x';
  1189. if Result [length (Result)] = '-' // remove '-' if all modifiers are 'On'
  1190. then System.Delete (Result, length (Result), 1);
  1191. end; { of function TRegExpr.GetModifierStr
  1192. --------------------------------------------------------------}
  1193. class function TRegExpr.ParseModifiersStr (const AModifiers : RegExprString;
  1194. var AModifiersInt : integer) : boolean;
  1195. // !!! Be carefull - this is class function and must not use object instance fields
  1196. var
  1197. i : integer;
  1198. IsOn : boolean;
  1199. Mask : integer;
  1200. begin
  1201. Result := true;
  1202. IsOn := true;
  1203. Mask := 0; // prevent compiler warning
  1204. for i := 1 to length (AModifiers) do
  1205. if AModifiers [i] = '-'
  1206. then IsOn := false
  1207. else begin
  1208. if Pos (AModifiers [i], 'iI') > 0
  1209. then Mask := MaskModI
  1210. else if Pos (AModifiers [i], 'rR') > 0
  1211. then Mask := MaskModR
  1212. else if Pos (AModifiers [i], 'sS') > 0
  1213. then Mask := MaskModS
  1214. else if Pos (AModifiers [i], 'gG') > 0
  1215. then Mask := MaskModG
  1216. else if Pos (AModifiers [i], 'mM') > 0
  1217. then Mask := MaskModM
  1218. else if Pos (AModifiers [i], 'xX') > 0
  1219. then Mask := MaskModX
  1220. else begin
  1221. Result := false;
  1222. EXIT;
  1223. end;
  1224. if IsOn
  1225. then AModifiersInt := AModifiersInt or Mask
  1226. else AModifiersInt := AModifiersInt and not Mask;
  1227. end;
  1228. end; { of function TRegExpr.ParseModifiersStr
  1229. --------------------------------------------------------------}
  1230. procedure TRegExpr.SetModifierStr (const AModifiers : RegExprString);
  1231. begin
  1232. if not ParseModifiersStr (AModifiers, fModifiers)
  1233. then Error (reeModifierUnsupported);
  1234. end; { of procedure TRegExpr.SetModifierStr
  1235. --------------------------------------------------------------}
  1236. function TRegExpr.GetModifier (AIndex : integer) : boolean;
  1237. var
  1238. Mask : integer;
  1239. begin
  1240. Result := false;
  1241. case AIndex of
  1242. 1: Mask := MaskModI;
  1243. 2: Mask := MaskModR;
  1244. 3: Mask := MaskModS;
  1245. 4: Mask := MaskModG;
  1246. 5: Mask := MaskModM;
  1247. 6: Mask := MaskModX;
  1248. else begin
  1249. Error (reeModifierUnsupported);
  1250. EXIT;
  1251. end;
  1252. end;
  1253. Result := (fModifiers and Mask) <> 0;
  1254. end; { of function TRegExpr.GetModifier
  1255. --------------------------------------------------------------}
  1256. procedure TRegExpr.SetModifier (AIndex : integer; ASet : boolean);
  1257. var
  1258. Mask : integer;
  1259. begin
  1260. case AIndex of
  1261. 1: Mask := MaskModI;
  1262. 2: Mask := MaskModR;
  1263. 3: Mask := MaskModS;
  1264. 4: Mask := MaskModG;
  1265. 5: Mask := MaskModM;
  1266. 6: Mask := MaskModX;
  1267. else begin
  1268. Error (reeModifierUnsupported);
  1269. EXIT;
  1270. end;
  1271. end;
  1272. if ASet
  1273. then fModifiers := fModifiers or Mask
  1274. else fModifiers := fModifiers and not Mask;
  1275. end; { of procedure TRegExpr.SetModifier
  1276. --------------------------------------------------------------}
  1277. {=============================================================}
  1278. {==================== Compiler section =======================}
  1279. {=============================================================}
  1280. function TRegExpr.IsSpaceChar(AChar: PRegExprChar): Boolean;
  1281. begin
  1282. Result:=Pos(AChar^,fSpaceChars)>0;
  1283. end;
  1284. procedure TRegExpr.InvalidateProgramm;
  1285. begin
  1286. if programm <> nil then begin
  1287. FreeMem (programm);
  1288. programm := nil;
  1289. end;
  1290. end; { of procedure TRegExpr.InvalidateProgramm
  1291. --------------------------------------------------------------}
  1292. procedure TRegExpr.Compile; //###0.941
  1293. begin
  1294. if fExpression = nil then begin // No Expression assigned
  1295. Error (reeNoExpression);
  1296. EXIT;
  1297. end;
  1298. CompileRegExpr (fExpression);
  1299. end; { of procedure TRegExpr.Compile
  1300. --------------------------------------------------------------}
  1301. function TRegExpr.IsProgrammOk : boolean;
  1302. {$IFNDEF UniCode}
  1303. var
  1304. i : integer;
  1305. {$ENDIF}
  1306. begin
  1307. Result := false;
  1308. // check modifiers
  1309. if fModifiers <> fProgModifiers //###0.941
  1310. then InvalidateProgramm;
  1311. // can we optimize line separators by using sets?
  1312. {$IFNDEF UniCode}
  1313. fLineSeparatorsSet := [];
  1314. for i := 1 to length (fLineSeparators)
  1315. do System.Include (fLineSeparatorsSet, fLineSeparators [i]);
  1316. {$ENDIF}
  1317. // [Re]compile if needed
  1318. if programm = nil
  1319. then Compile; //###0.941
  1320. // check [re]compiled programm
  1321. if programm = nil
  1322. then EXIT // error was set/raised by Compile (was reeExecAfterCompErr)
  1323. else if programm [0] <> MAGIC // Program corrupted.
  1324. then Error (reeCorruptedProgram)
  1325. else Result := true;
  1326. end; { of function TRegExpr.IsProgrammOk
  1327. --------------------------------------------------------------}
  1328. procedure TRegExpr.Tail (p : PRegExprChar; val : PRegExprChar);
  1329. // set the next-pointer at the end of a node chain
  1330. var
  1331. scan : PRegExprChar;
  1332. temp : PRegExprChar;
  1333. // i : int64;
  1334. begin
  1335. if p = @regdummy
  1336. then EXIT;
  1337. // Find last node.
  1338. scan := p;
  1339. REPEAT
  1340. temp := regnext (scan);
  1341. if temp = nil
  1342. then BREAK;
  1343. scan := temp;
  1344. UNTIL false;
  1345. // Set Next 'pointer'
  1346. if val < scan
  1347. then PRENextOff (AlignToPtr(scan + REOpSz))^ := - (scan - val) //###0.948
  1348. // work around PWideChar subtraction bug (Delphi uses
  1349. // shr after subtraction to calculate widechar distance %-( )
  1350. // so, if difference is negative we have .. the "feature" :(
  1351. // I could wrap it in $IFDEF UniCode, but I didn't because
  1352. // "P – Q computes the difference between the address given
  1353. // by P (the higher address) and the address given by Q (the
  1354. // lower address)" - Delphi help quotation.
  1355. else PRENextOff (AlignToPtr(scan + REOpSz))^ := val - scan; //###0.933
  1356. end; { of procedure TRegExpr.Tail
  1357. --------------------------------------------------------------}
  1358. procedure TRegExpr.OpTail (p : PRegExprChar; val : PRegExprChar);
  1359. // regtail on operand of first argument; nop if operandless
  1360. begin
  1361. // "Operandless" and "op != BRANCH" are synonymous in practice.
  1362. if (p = nil) or (p = @regdummy) or (PREOp (p)^ <> BRANCH)
  1363. then EXIT;
  1364. Tail (p + REOpSz + RENextOffSz, val); //###0.933
  1365. end; { of procedure TRegExpr.OpTail
  1366. --------------------------------------------------------------}
  1367. function TRegExpr.EmitNode (op : TREOp) : PRegExprChar; //###0.933
  1368. // emit a node, return location
  1369. begin
  1370. Result := regcode;
  1371. if Result <> @regdummy then begin
  1372. PREOp (regcode)^ := op;
  1373. inc (regcode, REOpSz);
  1374. PRENextOff (AlignToPtr(regcode))^ := 0; // Next "pointer" := nil
  1375. inc (regcode, RENextOffSz);
  1376. {$IFDEF DebugSynRegExpr}
  1377. if regcode-programm>regsize then
  1378. raise Exception.Create('TRegExpr.EmitNode buffer overrun');
  1379. {$ENDIF}
  1380. end
  1381. else inc (regsize, REOpSz + RENextOffSz); // compute code size without code generation
  1382. end; { of function TRegExpr.EmitNode
  1383. --------------------------------------------------------------}
  1384. procedure TRegExpr.EmitC (b : REChar);
  1385. // emit a byte to code
  1386. begin
  1387. if regcode <> @regdummy then begin
  1388. regcode^ := b;
  1389. inc (regcode);
  1390. {$IFDEF DebugSynRegExpr}
  1391. if regcode-programm>regsize then
  1392. raise Exception.Create('TRegExpr.EmitC buffer overrun');
  1393. {$ENDIF}
  1394. end
  1395. else inc (regsize, REOpSz); // Type of p-code pointer always is ^REChar
  1396. end; { of procedure TRegExpr.EmitC
  1397. --------------------------------------------------------------}
  1398. procedure TRegExpr.InsertOperator (op : TREOp; opnd : PRegExprChar; sz : integer);
  1399. // insert an operator in front of already-emitted operand
  1400. // Means relocating the operand.
  1401. var
  1402. src, dst, place : PRegExprChar;
  1403. i : integer;
  1404. begin
  1405. if regcode = @regdummy then begin
  1406. inc (regsize, sz);
  1407. EXIT;
  1408. end;
  1409. // move code behind insert position
  1410. src := regcode;
  1411. inc (regcode, sz);
  1412. {$IFDEF DebugSynRegExpr}
  1413. if regcode-programm>regsize then
  1414. raise Exception.Create('TRegExpr.InsertOperator buffer overrun');
  1415. // if (opnd<regcode) or (opnd-regcode>regsize) then
  1416. // raise Exception.Create('TRegExpr.InsertOperator invalid opnd');
  1417. {$ENDIF}
  1418. dst := regcode;
  1419. while src > opnd do begin
  1420. dec (dst);
  1421. dec (src);
  1422. dst^ := src^;
  1423. end;
  1424. place := opnd; // Op node, where operand used to be.
  1425. PREOp (place)^ := op;
  1426. inc (place, REOpSz);
  1427. for i := 1 + REOpSz to sz do begin
  1428. place^ := #0;
  1429. inc (place);
  1430. end;
  1431. end; { of procedure TRegExpr.InsertOperator
  1432. --------------------------------------------------------------}
  1433. function strcspn (s1 : PRegExprChar; s2 : PRegExprChar) : PtrInt;
  1434. // find length of initial segment of s1 consisting
  1435. // entirely of characters not from s2
  1436. var scan1, scan2 : PRegExprChar;
  1437. begin
  1438. Result := 0;
  1439. scan1 := s1;
  1440. while scan1^ <> #0 do begin
  1441. scan2 := s2;
  1442. while scan2^ <> #0 do
  1443. if scan1^ = scan2^
  1444. then EXIT
  1445. else inc (scan2);
  1446. inc (Result);
  1447. inc (scan1)
  1448. end;
  1449. end; { of function strcspn
  1450. --------------------------------------------------------------}
  1451. const
  1452. // Flags to be passed up and down.
  1453. HASWIDTH = 01; // Known never to match nil string.
  1454. SIMPLE = 02; // Simple enough to be STAR/PLUS/BRACES operand.
  1455. SPSTART = 04; // Starts with * or +.
  1456. WORST = 0; // Worst case.
  1457. META : array [0 .. 12] of REChar = (
  1458. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{', #0);
  1459. // Any modification must be synchronized with QuoteRegExprMetaChars !!!
  1460. {$IFDEF UniCode}
  1461. RusRangeLo : array [0 .. 33] of REChar =
  1462. (#$430,#$431,#$432,#$433,#$434,#$435,#$451,#$436,#$437,
  1463. #$438,#$439,#$43A,#$43B,#$43C,#$43D,#$43E,#$43F,
  1464. #$440,#$441,#$442,#$443,#$444,#$445,#$446,#$447,
  1465. #$448,#$449,#$44A,#$44B,#$44C,#$44D,#$44E,#$44F,#0);
  1466. RusRangeHi : array [0 .. 33] of REChar =
  1467. (#$410,#$411,#$412,#$413,#$414,#$415,#$401,#$416,#$417,
  1468. #$418,#$419,#$41A,#$41B,#$41C,#$41D,#$41E,#$41F,
  1469. #$420,#$421,#$422,#$423,#$424,#$425,#$426,#$427,
  1470. #$428,#$429,#$42A,#$42B,#$42C,#$42D,#$42E,#$42F,#0);
  1471. RusRangeLoLow = #$430{'а'};
  1472. RusRangeLoHigh = #$44F{'я'};
  1473. RusRangeHiLow = #$410{'А'};
  1474. RusRangeHiHigh = #$42F{'Я'};
  1475. {$ELSE}
  1476. RusRangeLo = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';
  1477. RusRangeHi = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ';
  1478. RusRangeLoLow = 'а';
  1479. RusRangeLoHigh = 'я';
  1480. RusRangeHiLow = 'А';
  1481. RusRangeHiHigh = 'Я';
  1482. {$ENDIF}
  1483. function TRegExpr.CompileRegExpr (exp : PRegExprChar) : boolean;
  1484. // Compile a regular expression into internal code
  1485. // We can't allocate space until we know how big the compiled form will be,
  1486. // but we can't compile it (and thus know how big it is) until we've got a
  1487. // place to put the code. So we cheat: we compile it twice, once with code
  1488. // generation turned off and size counting turned on, and once "for real".
  1489. // This also means that we don't allocate space until we are sure that the
  1490. // thing really will compile successfully, and we never have to move the
  1491. // code and thus invalidate pointers into it. (Note that it has to be in
  1492. // one piece because free() must be able to free it all.)
  1493. // Beware that the optimization-preparation code in here knows about some
  1494. // of the structure of the compiled regexp.
  1495. var
  1496. scan, longest : PRegExprChar;
  1497. len : PtrUInt;
  1498. flags : integer;
  1499. begin
  1500. Result := false; // life too dark
  1501. flags:=0;
  1502. regparse := nil; // for correct error handling
  1503. regexpbeg := exp;
  1504. try
  1505. if programm <> nil then begin
  1506. FreeMem (programm);
  1507. programm := nil;
  1508. end;
  1509. if exp = nil then begin
  1510. Error (reeCompNullArgument);
  1511. EXIT;
  1512. end;
  1513. fProgModifiers := fModifiers;
  1514. // well, may it's paranoia. I'll check it later... !!!!!!!!
  1515. // First pass: determine size, legality.
  1516. fCompModifiers := fModifiers;
  1517. regparse := exp;
  1518. regnpar := 1;
  1519. regsize := 0;
  1520. regcode := @regdummy;
  1521. EmitC (MAGIC);
  1522. if ParseReg (0, flags) = nil
  1523. then EXIT;
  1524. // Allocate space.
  1525. GetMem (programm, regsize * SizeOf (REChar));
  1526. // Second pass: emit code.
  1527. fCompModifiers := fModifiers;
  1528. regparse := exp;
  1529. regnpar := 1;
  1530. regcode := programm;
  1531. EmitC (MAGIC);
  1532. if ParseReg (0, flags) = nil
  1533. then EXIT;
  1534. // Dig out information for optimizations.
  1535. {$IFDEF UseFirstCharSet} //###0.929
  1536. FirstCharSet := [];
  1537. FillFirstCharSet (programm + REOpSz);
  1538. {$ENDIF}
  1539. regstart := #0; // Worst-case defaults.
  1540. reganch := #0;
  1541. regmust := nil;
  1542. regmlen := 0;
  1543. scan := programm + REOpSz; // First BRANCH.
  1544. if PREOp (regnext (scan))^ = EEND then begin // Only one top-level choice.
  1545. scan := scan + REOpSz + RENextOffSz;
  1546. // Starting-point info.
  1547. if PREOp (scan)^ = EXACTLY
  1548. then regstart := (scan + REOpSz + RENextOffSz)^
  1549. else if PREOp (scan)^ = BOL
  1550. then inc (reganch);
  1551. // If there's something expensive in the r.e., find the longest
  1552. // literal string that must appear and make it the regmust. Resolve
  1553. // ties in favor of later strings, since the regstart check works
  1554. // with the beginning of the r.e. and avoiding duplication
  1555. // strengthens checking. Not a strong reason, but sufficient in the
  1556. // absence of others.
  1557. if (flags and SPSTART) <> 0 then begin
  1558. longest := nil;
  1559. len := 0;
  1560. while scan <> nil do begin
  1561. if (PREOp (scan)^ = EXACTLY)
  1562. and (strlen (scan + REOpSz + RENextOffSz) >= PtrInt(len)) then begin
  1563. longest := scan + REOpSz + RENextOffSz;
  1564. len := strlen (longest);
  1565. end;
  1566. scan := regnext (scan);
  1567. end;
  1568. regmust := longest;
  1569. regmlen := len;
  1570. end;
  1571. end;
  1572. Result := true;
  1573. finally begin
  1574. if not Result
  1575. then InvalidateProgramm;
  1576. regexpbeg := nil;
  1577. fExprIsCompiled := Result; //###0.944
  1578. end;
  1579. end;
  1580. end; { of function TRegExpr.CompileRegExpr
  1581. --------------------------------------------------------------}
  1582. function TRegExpr.ParseReg (paren : integer; var flagp : integer) : PRegExprChar;
  1583. // regular expression, i.e. main body or parenthesized thing
  1584. // Caller must absorb opening parenthesis.
  1585. // Combining parenthesis handling with the base level of regular expression
  1586. // is a trifle forced, but the need to tie the tails of the branches to what
  1587. // follows makes it hard to avoid.
  1588. var
  1589. ret, br, ender : PRegExprChar;
  1590. parno : integer;
  1591. flags : integer;
  1592. SavedModifiers : integer;
  1593. begin
  1594. flags:=0;
  1595. Result := nil;
  1596. flagp := HASWIDTH; // Tentatively.
  1597. parno := 0; // eliminate compiler stupid warning
  1598. SavedModifiers := fCompModifiers;
  1599. // Make an OPEN node, if parenthesized.
  1600. if paren <> 0 then begin
  1601. if regnpar >= NSUBEXP then begin
  1602. Error (reeCompParseRegTooManyBrackets);
  1603. EXIT;
  1604. end;
  1605. parno := regnpar;
  1606. inc (regnpar);
  1607. ret := EmitNode (TREOp (ord (OPEN) + parno));
  1608. end
  1609. else ret := nil;
  1610. // Pick up the branches, linking them together.
  1611. br := ParseBranch (flags);
  1612. if br = nil then begin
  1613. Result := nil;
  1614. EXIT;
  1615. end;
  1616. if ret <> nil
  1617. then Tail (ret, br) // OPEN -> first.
  1618. else ret := br;
  1619. if (flags and HASWIDTH) = 0
  1620. then flagp := flagp and not HASWIDTH;
  1621. flagp := flagp or flags and SPSTART;
  1622. while (regparse^ = '|') do begin
  1623. inc (regparse);
  1624. br := ParseBranch (flags);
  1625. if br = nil then begin
  1626. Result := nil;
  1627. EXIT;
  1628. end;
  1629. Tail (ret, br); // BRANCH -> BRANCH.
  1630. if (flags and HASWIDTH) = 0
  1631. then flagp := flagp and not HASWIDTH;
  1632. flagp := flagp or flags and SPSTART;
  1633. end;
  1634. // Make a closing node, and hook it on the end.
  1635. if paren <> 0
  1636. then ender := EmitNode (TREOp (ord (CLOSE) + parno))
  1637. else ender := EmitNode (EEND);
  1638. Tail (ret, ender);
  1639. // Hook the tails of the branches to the closing node.
  1640. br := ret;
  1641. while br <> nil do begin
  1642. OpTail (br, ender);
  1643. br := regnext (br);
  1644. end;
  1645. // Check for proper termination.
  1646. if paren <> 0 then
  1647. if regparse^ <> ')' then begin
  1648. Error (reeCompParseRegUnmatchedBrackets);
  1649. EXIT;
  1650. end
  1651. else inc (regparse); // skip trailing ')'
  1652. if (paren = 0) and (regparse^ <> #0) then begin
  1653. if regparse^ = ')'
  1654. then Error (reeCompParseRegUnmatchedBrackets2)
  1655. else Error (reeCompParseRegJunkOnEnd);
  1656. EXIT;
  1657. end;
  1658. fCompModifiers := SavedModifiers; // restore modifiers of parent
  1659. Result := ret;
  1660. end; { of function TRegExpr.ParseReg
  1661. --------------------------------------------------------------}
  1662. function TRegExpr.ParseBranch (var flagp : integer) : PRegExprChar;
  1663. // one alternative of an | operator
  1664. // Implements the concatenation operator.
  1665. var
  1666. ret, chain, latest : PRegExprChar;
  1667. flags : integer;
  1668. begin
  1669. flags:=0;
  1670. flagp := WORST; // Tentatively.
  1671. ret := EmitNode (BRANCH);
  1672. chain := nil;
  1673. while (regparse^ <> #0) and (regparse^ <> '|')
  1674. and (regparse^ <> ')') do begin
  1675. latest := ParsePiece (flags);
  1676. if latest = nil then begin
  1677. Result := nil;
  1678. EXIT;
  1679. end;
  1680. flagp := flagp or flags and HASWIDTH;
  1681. if chain = nil // First piece.
  1682. then flagp := flagp or flags and SPSTART
  1683. else Tail (chain, latest);
  1684. chain := latest;
  1685. end;
  1686. if chain = nil // Loop ran zero times.
  1687. then EmitNode (NOTHING);
  1688. Result := ret;
  1689. end; { of function TRegExpr.ParseBranch
  1690. --------------------------------------------------------------}
  1691. function TRegExpr.ParsePiece (var flagp : integer) : PRegExprChar;
  1692. // something followed by possible [*+?{]
  1693. // Note that the branching code sequences used for ? and the general cases
  1694. // of * and + and { are somewhat optimized: they use the same NOTHING node as
  1695. // both the endmarker for their branch list and the body of the last branch.
  1696. // It might seem that this node could be dispensed with entirely, but the
  1697. // endmarker role is not redundant.
  1698. function parsenum (AStart, AEnd : PRegExprChar) : TREBracesArg;
  1699. begin
  1700. Result := 0;
  1701. if AEnd - AStart + 1 > 8 then begin // prevent stupid scanning
  1702. Error (reeBRACESArgTooBig);
  1703. EXIT;
  1704. end;
  1705. while AStart <= AEnd do begin
  1706. Result := Result * 10 + (ord (AStart^) - ord ('0'));
  1707. inc (AStart);
  1708. end;
  1709. if (Result > MaxBracesArg) or (Result < 0) then begin
  1710. Error (reeBRACESArgTooBig);
  1711. EXIT;
  1712. end;
  1713. end;
  1714. var
  1715. op : REChar;
  1716. NonGreedyOp, NonGreedyCh : boolean; //###0.940
  1717. TheOp : TREOp; //###0.940
  1718. NextNode : PRegExprChar;
  1719. flags : integer;
  1720. BracesMin, Bracesmax : TREBracesArg;
  1721. p, savedparse : PRegExprChar;
  1722. procedure EmitComplexBraces (ABracesMin, ABracesMax : TREBracesArg;
  1723. ANonGreedyOp : boolean); //###0.940
  1724. {$IFDEF ComplexBraces}
  1725. var
  1726. off : TRENextOff;
  1727. {$ENDIF}
  1728. begin
  1729. {$IFNDEF ComplexBraces}
  1730. Error (reeComplexBracesNotImplemented);
  1731. {$ELSE}
  1732. if ANonGreedyOp
  1733. then TheOp := LOOPNG
  1734. else TheOp := LOOP;
  1735. InsertOperator (LOOPENTRY, Result, REOpSz + RENextOffSz);
  1736. NextNode := EmitNode (TheOp);
  1737. if regcode <> @regdummy then begin
  1738. off := (Result + REOpSz + RENextOffSz)
  1739. - (regcode - REOpSz - RENextOffSz); // back to Atom after LOOPENTRY
  1740. PREBracesArg (AlignToInt(regcode))^ := ABracesMin;
  1741. inc (regcode, REBracesArgSz);
  1742. PREBracesArg (AlignToInt(regcode))^ := ABracesMax;
  1743. inc (regcode, REBracesArgSz);
  1744. PRENextOff (AlignToPtr(regcode))^ := off;
  1745. inc (regcode, RENextOffSz);
  1746. {$IFDEF DebugSynRegExpr}
  1747. if regcode-programm>regsize then
  1748. raise Exception.Create('TRegExpr.ParsePiece.EmitComplexBraces buffer overrun');
  1749. {$ENDIF}
  1750. end
  1751. else inc (regsize, REBracesArgSz * 2 + RENextOffSz);
  1752. Tail (Result, NextNode); // LOOPENTRY -> LOOP
  1753. if regcode <> @regdummy then
  1754. Tail (Result + REOpSz + RENextOffSz, NextNode); // Atom -> LOOP
  1755. {$ENDIF}
  1756. end;
  1757. procedure EmitSimpleBraces (ABracesMin, ABracesMax : TREBracesArg;
  1758. ANonGreedyOp : boolean); //###0.940
  1759. begin
  1760. if ANonGreedyOp //###0.940
  1761. then TheOp := BRACESNG
  1762. else TheOp := BRACES;
  1763. InsertOperator (TheOp, Result, REOpSz + RENextOffSz + REBracesArgSz * 2);
  1764. if regcode <> @regdummy then begin
  1765. PREBracesArg (AlignToInt(Result + REOpSz + RENextOffSz))^ := ABracesMin;
  1766. PREBracesArg (AlignToInt(Result + REOpSz + RENextOffSz + REBracesArgSz))^ := ABracesMax;
  1767. end;
  1768. end;
  1769. begin
  1770. flags:=0;
  1771. Result := ParseAtom (flags);
  1772. if Result = nil
  1773. then EXIT;
  1774. op := regparse^;
  1775. if not ((op = '*') or (op = '+') or (op = '?') or (op = '{')) then begin
  1776. flagp := flags;
  1777. EXIT;
  1778. end;
  1779. if ((flags and HASWIDTH) = 0) and (op <> '?') then begin
  1780. Error (reePlusStarOperandCouldBeEmpty);
  1781. EXIT;
  1782. end;
  1783. case op of
  1784. '*': begin
  1785. flagp := WORST or SPSTART;
  1786. NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
  1787. NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
  1788. if (flags and SIMPLE) = 0 then begin
  1789. if NonGreedyOp //###0.940
  1790. then EmitComplexBraces (0, MaxBracesArg, NonGreedyOp)
  1791. else begin // Emit x* as (x&|), where & means "self".
  1792. InsertOperator (BRANCH, Result, REOpSz + RENextOffSz); // Either x
  1793. OpTail (Result, EmitNode (BACK)); // and loop
  1794. OpTail (Result, Result); // back
  1795. Tail (Result, EmitNode (BRANCH)); // or
  1796. Tail (Result, EmitNode (NOTHING)); // nil.
  1797. end
  1798. end
  1799. else begin // Simple
  1800. if NonGreedyOp //###0.940
  1801. then TheOp := STARNG
  1802. else TheOp := STAR;
  1803. InsertOperator (TheOp, Result, REOpSz + RENextOffSz);
  1804. end;
  1805. if NonGreedyCh //###0.940
  1806. then inc (regparse); // Skip extra char ('?')
  1807. end; { of case '*'}
  1808. '+': begin
  1809. flagp := WORST or SPSTART or HASWIDTH;
  1810. NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
  1811. NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
  1812. if (flags and SIMPLE) = 0 then begin
  1813. if NonGreedyOp //###0.940
  1814. then EmitComplexBraces (1, MaxBracesArg, NonGreedyOp)
  1815. else begin // Emit x+ as x(&|), where & means "self".
  1816. NextNode := EmitNode (BRANCH); // Either
  1817. Tail (Result, NextNode);
  1818. Tail (EmitNode (BACK), Result); // loop back
  1819. Tail (NextNode, EmitNode (BRANCH)); // or
  1820. Tail (Result, EmitNode (NOTHING)); // nil.
  1821. end
  1822. end
  1823. else begin // Simple
  1824. if NonGreedyOp //###0.940
  1825. then TheOp := PLUSNG
  1826. else TheOp := PLUS;
  1827. InsertOperator (TheOp, Result, REOpSz + RENextOffSz);
  1828. end;
  1829. if NonGreedyCh //###0.940
  1830. then inc (regparse); // Skip extra char ('?')
  1831. end; { of case '+'}
  1832. '?': begin
  1833. flagp := WORST;
  1834. NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
  1835. NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
  1836. if NonGreedyOp then begin //###0.940 // We emit x?? as x{0,1}?
  1837. if (flags and SIMPLE) = 0
  1838. then EmitComplexBraces (0, 1, NonGreedyOp)
  1839. else EmitSimpleBraces (0, 1, NonGreedyOp);
  1840. end
  1841. else begin // greedy '?'
  1842. InsertOperator (BRANCH, Result, REOpSz + RENextOffSz); // Either x
  1843. Tail (Result, EmitNode (BRANCH)); // or
  1844. NextNode := EmitNode (NOTHING); // nil.
  1845. Tail (Result, NextNode);
  1846. OpTail (Result, NextNode);
  1847. end;
  1848. if NonGreedyCh //###0.940
  1849. then inc (regparse); // Skip extra char ('?')
  1850. end; { of case '?'}
  1851. '{': begin
  1852. savedparse := regparse;
  1853. // !!!!!!!!!!!!
  1854. // Filip Jirsak's note - what will happen, when we are at the end of regparse?
  1855. inc (regparse);
  1856. p := regparse;
  1857. while Pos (regparse^, '0123456789') > 0 // <min> MUST appear
  1858. do inc (regparse);
  1859. if (regparse^ <> '}') and (regparse^ <> ',') or (p = regparse) then begin
  1860. regparse := savedparse;
  1861. flagp := flags;
  1862. EXIT;
  1863. end;
  1864. BracesMin := parsenum (p, regparse - 1);
  1865. if regparse^ = ',' then begin
  1866. inc (regparse);
  1867. p := regparse;
  1868. while Pos (regparse^, '0123456789') > 0
  1869. do inc (regparse);
  1870. if regparse^ <> '}' then begin
  1871. regparse := savedparse;
  1872. EXIT;
  1873. end;
  1874. if p = regparse
  1875. then BracesMax := MaxBracesArg
  1876. else BracesMax := parsenum (p, regparse - 1);
  1877. end
  1878. else BracesMax := BracesMin; // {n} == {n,n}
  1879. if BracesMin > BracesMax then begin
  1880. Error (reeBracesMinParamGreaterMax);
  1881. EXIT;
  1882. end;
  1883. if BracesMin > 0
  1884. then flagp := WORST;
  1885. if BracesMax > 0
  1886. then flagp := flagp or HASWIDTH or SPSTART;
  1887. NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
  1888. NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
  1889. if (flags and SIMPLE) <> 0
  1890. then EmitSimpleBraces (BracesMin, BracesMax, NonGreedyOp)
  1891. else EmitComplexBraces (BracesMin, BracesMax, NonGreedyOp);
  1892. if NonGreedyCh //###0.940
  1893. then inc (regparse); // Skip extra char '?'
  1894. end; // of case '{'
  1895. // else // here we can't be
  1896. end; { of case op}
  1897. inc (regparse);
  1898. if (regparse^ = '*') or (regparse^ = '+') or (regparse^ = '?') or (regparse^ = '{') then begin
  1899. Error (reeNestedSQP);
  1900. EXIT;
  1901. end;
  1902. end; { of function TRegExpr.ParsePiece
  1903. --------------------------------------------------------------}
  1904. function TRegExpr.ParseAtom (var flagp : integer) : PRegExprChar;
  1905. // the lowest level
  1906. // Optimization: gobbles an entire sequence of ordinary characters so that
  1907. // it can turn them into a single node, which is smaller to store and
  1908. // faster to run. Backslashed characters are exceptions, each becoming a
  1909. // separate node; the code is simpler that way and it's not worth fixing.
  1910. var
  1911. ret : PRegExprChar;
  1912. flags : integer;
  1913. RangeBeg, RangeEnd : REChar;
  1914. CanBeRange : boolean;
  1915. len : PtrInt;
  1916. ender : REChar;
  1917. begmodfs : PRegExprChar;
  1918. {$IFDEF UseSetOfChar} //###0.930
  1919. RangePCodeBeg : PRegExprChar;
  1920. RangePCodeIdx : PtrInt;
  1921. RangeIsCI : boolean;
  1922. RangeSet : TSetOfREChar;
  1923. RangeLen : PtrInt;
  1924. RangeChMin, RangeChMax : REChar;
  1925. {$ENDIF}
  1926. procedure EmitExactly (ch : REChar);
  1927. begin
  1928. if (fCompModifiers and MaskModI) <> 0
  1929. then ret := EmitNode (EXACTLYCI)
  1930. else ret := EmitNode (EXACTLY);
  1931. EmitC (ch);
  1932. EmitC (#0);
  1933. flagp := flagp or HASWIDTH or SIMPLE;
  1934. end;
  1935. procedure EmitStr (const s : RegExprString);
  1936. var i : PtrInt;
  1937. begin
  1938. for i := 1 to length (s)
  1939. do EmitC (s [i]);
  1940. end;
  1941. function HexDig (ch : REChar) : PtrInt;
  1942. begin
  1943. Result := 0;
  1944. if (ch >= 'a') and (ch <= 'f')
  1945. then ch := REChar (ord (ch) - (ord ('a') - ord ('A')));
  1946. if (ch < '0') or (ch > 'F') or ((ch > '9') and (ch < 'A')) then begin
  1947. Error (reeBadHexDigit);
  1948. EXIT;
  1949. end;
  1950. Result := ord (ch) - ord ('0');
  1951. if ch >= 'A'
  1952. then Result := Result - (ord ('A') - ord ('9') - 1);
  1953. end;
  1954. function EmitRange (AOpCode : REChar) : PRegExprChar;
  1955. begin
  1956. {$IFDEF UseSetOfChar}
  1957. case AOpCode of
  1958. ANYBUTCI, ANYBUT:
  1959. Result := EmitNode (ANYBUTTINYSET);
  1960. else // ANYOFCI, ANYOF
  1961. Result := EmitNode (ANYOFTINYSET);
  1962. end;
  1963. case AOpCode of
  1964. ANYBUTCI, ANYOFCI:
  1965. RangeIsCI := True;
  1966. else // ANYBUT, ANYOF
  1967. RangeIsCI := False;
  1968. end;
  1969. RangePCodeBeg := regcode;
  1970. RangePCodeIdx := regsize;
  1971. RangeLen := 0;
  1972. RangeSet := [];
  1973. RangeChMin := #255;
  1974. RangeChMax := #0;
  1975. {$ELSE}
  1976. Result := EmitNode (AOpCode);
  1977. // ToDo:
  1978. // !!!!!!!!!!!!! Implement ANYOF[BUT]TINYSET generation for UniCode !!!!!!!!!!
  1979. {$ENDIF}
  1980. end;
  1981. {$IFDEF UseSetOfChar}
  1982. procedure EmitRangeCPrim (b : REChar); //###0.930
  1983. begin
  1984. if b in RangeSet
  1985. then EXIT;
  1986. inc (RangeLen);
  1987. if b < RangeChMin
  1988. then RangeChMin := b;
  1989. if b > RangeChMax
  1990. then RangeChMax := b;
  1991. Include (RangeSet, b);
  1992. end;
  1993. {$ENDIF}
  1994. procedure EmitRangeC (b : REChar);
  1995. {$IFDEF UseSetOfChar}
  1996. var
  1997. Ch : REChar;
  1998. {$ENDIF}
  1999. begin
  2000. CanBeRange := false;
  2001. {$IFDEF UseSetOfChar}
  2002. if b <> #0 then begin
  2003. EmitRangeCPrim (b); //###0.930
  2004. if RangeIsCI
  2005. then EmitRangeCPrim (InvertCase (b)); //###0.930
  2006. end
  2007. else begin
  2008. {$IFDEF UseAsserts}
  2009. Assert (RangeLen > 0, 'TRegExpr.ParseAtom(subroutine EmitRangeC): empty range'); // impossible, but who knows..
  2010. Assert (RangeChMin <= RangeChMax, 'TRegExpr.ParseAtom(subroutine EmitRangeC): RangeChMin > RangeChMax'); // impossible, but who knows..
  2011. {$ENDIF}
  2012. if RangeLen <= TinySetLen then begin // emit "tiny set"
  2013. if regcode = @regdummy then begin
  2014. regsize := RangePCodeIdx + TinySetLen; // RangeChMin/Max !!!
  2015. EXIT;
  2016. end;
  2017. regcode := RangePCodeBeg;
  2018. for Ch := RangeChMin to RangeChMax do //###0.930
  2019. if Ch in RangeSet then begin
  2020. regcode^ := Ch;
  2021. inc (regcode);
  2022. end;
  2023. // fill rest:
  2024. while regcode < RangePCodeBeg + TinySetLen do begin
  2025. regcode^ := RangeChMax;
  2026. inc (regcode);
  2027. end;
  2028. {$IFDEF DebugSynRegExpr}
  2029. if regcode-programm>regsize then
  2030. raise Exception.Create('TRegExpr.ParseAtom.EmitRangeC TinySetLen buffer overrun');
  2031. {$ENDIF}
  2032. end
  2033. else begin
  2034. if regcode = @regdummy then begin
  2035. regsize := RangePCodeIdx + SizeOf (TSetOfREChar);
  2036. EXIT;
  2037. end;
  2038. if (RangePCodeBeg - REOpSz - RENextOffSz)^ = ANYBUTTINYSET
  2039. then RangeSet := [#0 .. #255] - RangeSet;
  2040. PREOp (RangePCodeBeg - REOpSz - RENextOffSz)^ := ANYOFFULLSET;
  2041. regcode := RangePCodeBeg;
  2042. Move (RangeSet, regcode^, SizeOf (TSetOfREChar));
  2043. inc (regcode, SizeOf (TSetOfREChar));
  2044. {$IFDEF DebugSynRegExpr}
  2045. if regcode-programm>regsize then
  2046. raise Exception.Create('TRegExpr.ParseAtom.EmitRangeC non TinySetLen buffer overrun');
  2047. {$ENDIF}
  2048. end;
  2049. end;
  2050. {$ELSE}
  2051. EmitC (b);
  2052. {$ENDIF}
  2053. end;
  2054. procedure EmitSimpleRangeC (b : REChar);
  2055. begin
  2056. RangeBeg := b;
  2057. EmitRangeC (b);
  2058. CanBeRange := true;
  2059. end;
  2060. procedure EmitRangeStr (const s : RegExprString);
  2061. var i : PtrInt;
  2062. begin
  2063. for i := 1 to length (s)
  2064. do EmitRangeC (s [i]);
  2065. end;
  2066. function UnQuoteChar (var APtr : PRegExprChar) : REChar; //###0.934
  2067. begin
  2068. case APtr^ of
  2069. 't': Result := #$9; // \t => tab (HT/TAB)
  2070. 'n': Result := #$a; // \n => newline (NL)
  2071. 'r': Result := #$d; // \r => carriage return (CR)
  2072. 'f': Result := #$c; // \f => form feed (FF)
  2073. 'a': Result := #$7; // \a => alarm (bell) (BEL)
  2074. 'e': Result := #$1b; // \e => escape (ESC)
  2075. 'x': begin // \x: hex char
  2076. Result := #0;
  2077. inc (APtr);
  2078. if APtr^ = #0 then begin
  2079. Error (reeNoHexCodeAfterBSlashX);
  2080. EXIT;
  2081. end;
  2082. if APtr^ = '{' then begin // \x{nnnn} //###0.936
  2083. REPEAT
  2084. inc (APtr);
  2085. if APtr^ = #0 then begin
  2086. Error (reeNoHexCodeAfterBSlashX);
  2087. EXIT;
  2088. end;
  2089. if APtr^ <> '}' then begin
  2090. if (Ord (Result)
  2091. ShR (SizeOf (REChar) * 8 - 4)) and $F <> 0 then begin
  2092. Error (reeHexCodeAfterBSlashXTooBig);
  2093. EXIT;
  2094. end;
  2095. Result := REChar ((Ord (Result) ShL 4) or HexDig (APtr^));
  2096. // HexDig will cause Error if bad hex digit found
  2097. end
  2098. else BREAK;
  2099. UNTIL False;
  2100. end
  2101. else begin
  2102. Result := REChar (HexDig (APtr^));
  2103. // HexDig will cause Error if bad hex digit found
  2104. inc (APtr);
  2105. if APtr^ = #0 then begin
  2106. Error (reeNoHexCodeAfterBSlashX);
  2107. EXIT;
  2108. end;
  2109. Result := REChar ((Ord (Result) ShL 4) or HexDig (APtr^));
  2110. // HexDig will cause Error if bad hex digit found
  2111. end;
  2112. end;
  2113. else Result := APtr^;
  2114. end;
  2115. end;
  2116. begin
  2117. Result := nil;
  2118. flags:=0;
  2119. flagp := WORST; // Tentatively.
  2120. inc (regparse);
  2121. case (regparse - 1)^ of
  2122. '^': if ((fCompModifiers and MaskModM) = 0)
  2123. or ((fLineSeparators = '') and not fLinePairedSeparatorAssigned)
  2124. then ret := EmitNode (BOL)
  2125. else ret := EmitNode (BOLML);
  2126. '$': if ((fCompModifiers and MaskModM) = 0)
  2127. or ((fLineSeparators = '') and not fLinePairedSeparatorAssigned)
  2128. then ret := EmitNode (EOL)
  2129. else ret := EmitNode (EOLML);
  2130. '.':
  2131. if (fCompModifiers and MaskModS) <> 0 then begin
  2132. ret := EmitNode (ANY);
  2133. flagp := flagp or HASWIDTH or SIMPLE;
  2134. end
  2135. else begin // not /s, so emit [^:LineSeparators:]
  2136. ret := EmitNode (ANYML);
  2137. flagp := flagp or HASWIDTH; // not so simple ;)
  2138. // ret := EmitRange (ANYBUT);
  2139. // EmitRangeStr (LineSeparators); //###0.941
  2140. // EmitRangeStr (LinePairedSeparator); // !!! isn't correct if have to accept only paired
  2141. // EmitRangeC (#0);
  2142. // flagp := flagp or HASWIDTH or SIMPLE;
  2143. end;
  2144. '[': begin
  2145. if regparse^ = '^' then begin // Complement of range.
  2146. if (fCompModifiers and MaskModI) <> 0
  2147. then ret := EmitRange (ANYBUTCI)
  2148. else ret := EmitRange (ANYBUT);
  2149. inc (regparse);
  2150. end
  2151. else
  2152. if (fCompModifiers and MaskModI) <> 0
  2153. then ret := EmitRange (ANYOFCI)
  2154. else ret := EmitRange (ANYOF);
  2155. CanBeRange := false;
  2156. if (regparse^ = ']') then begin
  2157. EmitSimpleRangeC (regparse^); // []-a] -> ']' .. 'a'
  2158. inc (regparse);
  2159. end;
  2160. while (regparse^ <> #0) and (regparse^ <> ']') do begin
  2161. if (regparse^ = '-')
  2162. and ((regparse + 1)^ <> #0) and ((regparse + 1)^ <> ']')
  2163. and CanBeRange then begin
  2164. inc (regparse);
  2165. RangeEnd := regparse^;
  2166. if RangeEnd = EscChar then begin
  2167. {$IFDEF UniCode} //###0.935
  2168. if (ord ((regparse + 1)^) < 256)
  2169. and (char ((regparse + 1)^)
  2170. in ['d', 'D', 's', 'S', 'w', 'W']) then begin
  2171. {$ELSE}
  2172. if (regparse + 1)^ in ['d', 'D', 's', 'S', 'w', 'W'] then begin
  2173. {$ENDIF}
  2174. EmitRangeC ('-'); // or treat as error ?!!
  2175. CONTINUE;
  2176. end;
  2177. inc (regparse);
  2178. RangeEnd := UnQuoteChar (regparse);
  2179. end;
  2180. // r.e.ranges extension for russian
  2181. if ((fCompModifiers and MaskModR) <> 0)
  2182. and (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeLoHigh) then begin
  2183. EmitRangeStr (RusRangeLo);
  2184. end
  2185. else if ((fCompModifiers and MaskModR) <> 0)
  2186. and (RangeBeg = RusRangeHiLow) and (RangeEnd = RusRangeHiHigh) then begin
  2187. EmitRangeStr (RusRangeHi);
  2188. end
  2189. else if ((fCompModifiers and MaskModR) <> 0)
  2190. and (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeHiHigh) then begin
  2191. EmitRangeStr (RusRangeLo);
  2192. EmitRangeStr (RusRangeHi);
  2193. end
  2194. else begin // standard r.e. handling
  2195. if RangeBeg > RangeEnd then begin
  2196. Error (reeInvalidRange);
  2197. EXIT;
  2198. end;
  2199. inc (RangeBeg);
  2200. EmitRangeC (RangeEnd); // prevent infinite loop if RangeEnd=$ff
  2201. while RangeBeg < RangeEnd do begin //###0.929
  2202. EmitRangeC (RangeBeg);
  2203. inc (RangeBeg);
  2204. end;
  2205. end;
  2206. inc (regparse);
  2207. end
  2208. else begin
  2209. if regparse^ = EscChar then begin
  2210. inc (regparse);
  2211. if regparse^ = #0 then begin
  2212. Error (reeParseAtomTrailingBackSlash);
  2213. EXIT;
  2214. end;
  2215. case regparse^ of // r.e.extensions
  2216. 'd': EmitRangeStr ('0123456789');
  2217. 'w': EmitRangeStr (WordChars);
  2218. 's': EmitRangeStr (SpaceChars);
  2219. else EmitSimpleRangeC (UnQuoteChar (regparse));
  2220. end; { of case}
  2221. end
  2222. else EmitSimpleRangeC (regparse^);
  2223. inc (regparse);
  2224. end;
  2225. end; { of while}
  2226. EmitRangeC (#0);
  2227. if regparse^ <> ']' then begin
  2228. Error (reeUnmatchedSqBrackets);
  2229. EXIT;
  2230. end;
  2231. inc (regparse);
  2232. flagp := flagp or HASWIDTH or SIMPLE;
  2233. end;
  2234. '(': begin
  2235. if regparse^ = '?' then begin
  2236. // check for extended Perl syntax : (?..)
  2237. if (regparse + 1)^ = '#' then begin // (?#comment)
  2238. inc (regparse, 2); // find closing ')'
  2239. while (regparse^ <> #0) and (regparse^ <> ')')
  2240. do inc (regparse);
  2241. if regparse^ <> ')' then begin
  2242. Error (reeUnclosedComment);
  2243. EXIT;
  2244. end;
  2245. inc (regparse); // skip ')'
  2246. ret := EmitNode (COMMENT); // comment
  2247. end
  2248. else begin // modifiers ?
  2249. inc (regparse); // skip '?'
  2250. begmodfs := regparse;
  2251. while (regparse^ <> #0) and (regparse^ <> ')')
  2252. do inc (regparse);
  2253. if (regparse^ <> ')')
  2254. or not ParseModifiersStr (copy (begmodfs, 1, (regparse - begmodfs)), fCompModifiers) then begin
  2255. Error (reeUrecognizedModifier);
  2256. EXIT;
  2257. end;
  2258. inc (regparse); // skip ')'
  2259. ret := EmitNode (COMMENT); // comment
  2260. // Error (reeQPSBFollowsNothing);
  2261. // EXIT;
  2262. end;
  2263. end
  2264. else begin
  2265. ret := ParseReg (1, flags);
  2266. if ret = nil then begin
  2267. Result := nil;
  2268. EXIT;
  2269. end;
  2270. flagp := flagp or flags and (HASWIDTH or SPSTART);
  2271. end;
  2272. end;
  2273. #0, '|', ')': begin // Supposed to be caught earlier.
  2274. Error (reeInternalUrp);
  2275. EXIT;
  2276. end;
  2277. '?', '+', '*': begin
  2278. Error (reeQPSBFollowsNothing);
  2279. EXIT;
  2280. end;
  2281. EscChar: begin
  2282. if regparse^ = #0 then begin
  2283. Error (reeTrailingBackSlash);
  2284. EXIT;
  2285. end;
  2286. case regparse^ of // r.e.extensions
  2287. 'b': ret := EmitNode (BOUND); //###0.943
  2288. 'B': ret := EmitNode (NOTBOUND); //###0.943
  2289. 'A': ret := EmitNode (BOL); //###0.941
  2290. 'Z': ret := EmitNode (EOL); //###0.941
  2291. 'd': begin // r.e.extension - any digit ('0' .. '9')
  2292. ret := EmitNode (ANYDIGIT);
  2293. flagp := flagp or HASWIDTH or SIMPLE;
  2294. end;
  2295. 'D': begin // r.e.extension - not digit ('0' .. '9')
  2296. ret := EmitNode (NOTDIGIT);
  2297. flagp := flagp or HASWIDTH or SIMPLE;
  2298. end;
  2299. 's': begin // r.e.extension - any space char
  2300. {$IFDEF UseSetOfChar}
  2301. ret := EmitRange (ANYOF);
  2302. EmitRangeStr (SpaceChars);
  2303. EmitRangeC (#0);
  2304. {$ELSE}
  2305. ret := EmitNode (ANYSPACE);
  2306. {$ENDIF}
  2307. flagp := flagp or HASWIDTH or SIMPLE;
  2308. end;
  2309. 'S': begin // r.e.extension - not space char
  2310. {$IFDEF UseSetOfChar}
  2311. ret := EmitRange (ANYBUT);
  2312. EmitRangeStr (SpaceChars);
  2313. EmitRangeC (#0);
  2314. {$ELSE}
  2315. ret := EmitNode (NOTSPACE);
  2316. {$ENDIF}
  2317. flagp := flagp or HASWIDTH or SIMPLE;
  2318. end;
  2319. 'w': begin // r.e.extension - any english char / digit / '_'
  2320. {$IFDEF UseSetOfChar}
  2321. ret := EmitRange (ANYOF);
  2322. EmitRangeStr (WordChars);
  2323. EmitRangeC (#0);
  2324. {$ELSE}
  2325. ret := EmitNode (ANYLETTER);
  2326. {$ENDIF}
  2327. flagp := flagp or HASWIDTH or SIMPLE;
  2328. end;
  2329. 'W': begin // r.e.extension - not english char / digit / '_'
  2330. {$IFDEF UseSetOfChar}
  2331. ret := EmitRange (ANYBUT);
  2332. EmitRangeStr (WordChars);
  2333. EmitRangeC (#0);
  2334. {$ELSE}
  2335. ret := EmitNode (NOTLETTER);
  2336. {$ENDIF}
  2337. flagp := flagp or HASWIDTH or SIMPLE;
  2338. end;
  2339. '1' .. '9': begin //###0.936
  2340. if (fCompModifiers and MaskModI) <> 0
  2341. then ret := EmitNode (BSUBEXPCI)
  2342. else ret := EmitNode (BSUBEXP);
  2343. EmitC (REChar (ord (regparse^) - ord ('0')));
  2344. flagp := flagp or HASWIDTH or SIMPLE;
  2345. end;
  2346. else EmitExactly (UnQuoteChar (regparse));
  2347. end; { of case}
  2348. inc (regparse);
  2349. end;
  2350. else begin
  2351. dec (regparse);
  2352. if ((fCompModifiers and MaskModX) <> 0) and // check for eXtended syntax
  2353. ((regparse^ = '#')
  2354. or ({$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947
  2355. {$ELSE}regparse^ in XIgnoredChars{$ENDIF})) then begin //###0.941 \x
  2356. if regparse^ = '#' then begin // Skip eXtended comment
  2357. // find comment terminator (group of \n and/or \r)
  2358. while (regparse^ <> #0) and (regparse^ <> #$d) and (regparse^ <> #$a)
  2359. do inc (regparse);
  2360. while (regparse^ = #$d) or (regparse^ = #$a) // skip comment terminator
  2361. do inc (regparse); // attempt to support different type of line separators
  2362. end
  2363. else begin // Skip the blanks!
  2364. while {$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947
  2365. {$ELSE}regparse^ in XIgnoredChars{$ENDIF}
  2366. do inc (regparse);
  2367. end;
  2368. ret := EmitNode (COMMENT); // comment
  2369. end
  2370. else begin
  2371. len := strcspn (regparse, META);
  2372. if len <= 0 then
  2373. if regparse^ <> '{' then begin
  2374. Error (reeRarseAtomInternalDisaster);
  2375. EXIT;
  2376. end
  2377. else len := strcspn (regparse + 1, META) + 1; // bad {n,m} - compile as EXATLY
  2378. ender := (regparse + len)^;
  2379. if (len > 1)
  2380. and ((ender = '*') or (ender = '+') or (ender = '?') or (ender = '{'))
  2381. then dec (len); // Back off clear of ?+*{ operand.
  2382. flagp := flagp or HASWIDTH;
  2383. if len = 1
  2384. then flagp := flagp or SIMPLE;
  2385. if (fCompModifiers and MaskModI) <> 0
  2386. then ret := EmitNode (EXACTLYCI)
  2387. else ret := EmitNode (EXACTLY);
  2388. while (len > 0)
  2389. and (((fCompModifiers and MaskModX) = 0) or (regparse^ <> '#')) do begin
  2390. if ((fCompModifiers and MaskModX) = 0) or not ( //###0.941
  2391. {$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947
  2392. {$ELSE}regparse^ in XIgnoredChars{$ENDIF} )
  2393. then EmitC (regparse^);
  2394. inc (regparse);
  2395. dec (len);
  2396. end;
  2397. EmitC (#0);
  2398. end; { of if not comment}
  2399. end; { of case else}
  2400. end; { of case}
  2401. Result := ret;
  2402. end; { of function TRegExpr.ParseAtom
  2403. --------------------------------------------------------------}
  2404. function TRegExpr.GetCompilerErrorPos : PtrInt;
  2405. begin
  2406. Result := 0;
  2407. if (regexpbeg = nil) or (regparse = nil)
  2408. then EXIT; // not in compiling mode ?
  2409. Result := regparse - regexpbeg;
  2410. end; { of function TRegExpr.GetCompilerErrorPos
  2411. --------------------------------------------------------------}
  2412. {=============================================================}
  2413. {===================== Matching section ======================}
  2414. {=============================================================}
  2415. {$IFNDEF UseSetOfChar}
  2416. function TRegExpr.StrScanCI (s : PRegExprChar; ch : REChar) : PRegExprChar; //###0.928 - now method of TRegExpr
  2417. begin
  2418. while (s^ <> #0) and (s^ <> ch) and (s^ <> InvertCase (ch))
  2419. do inc (s);
  2420. if s^ <> #0
  2421. then Result := s
  2422. else Result := nil;
  2423. end; { of function TRegExpr.StrScanCI
  2424. --------------------------------------------------------------}
  2425. {$ENDIF}
  2426. function TRegExpr.regrepeat (p : PRegExprChar; AMax : PtrInt) : PtrInt;
  2427. // repeatedly match something simple, report how many
  2428. var
  2429. scan : PRegExprChar;
  2430. opnd : PRegExprChar;
  2431. TheMax : integer;
  2432. {Ch,} InvCh : REChar; //###0.931
  2433. sestart, seend : PRegExprChar; //###0.936
  2434. begin
  2435. Result := 0;
  2436. scan := reginput;
  2437. opnd := p + REOpSz + RENextOffSz; //OPERAND
  2438. TheMax := fInputEnd - scan;
  2439. if TheMax > AMax
  2440. then TheMax := AMax;
  2441. case PREOp (p)^ of
  2442. ANY: begin
  2443. // note - ANYML cannot be proceeded in regrepeat because can skip
  2444. // more than one char at once
  2445. Result := TheMax;
  2446. inc (scan, Result);
  2447. end;
  2448. EXACTLY: begin // in opnd can be only ONE char !!!
  2449. // Ch := opnd^; // store in register //###0.931
  2450. while (Result < TheMax) and (opnd^ = scan^) do begin
  2451. inc (Result);
  2452. inc (scan);
  2453. end;
  2454. end;
  2455. EXACTLYCI: begin // in opnd can be only ONE char !!!
  2456. // Ch := opnd^; // store in register //###0.931
  2457. while (Result < TheMax) and (opnd^ = scan^) do begin // prevent unneeded InvertCase //###0.931
  2458. inc (Result);
  2459. inc (scan);
  2460. end;
  2461. if Result < TheMax then begin //###0.931
  2462. InvCh := InvertCase (opnd^); // store in register
  2463. while (Result < TheMax) and
  2464. ((opnd^ = scan^) or (InvCh = scan^)) do begin
  2465. inc (Result);
  2466. inc (scan);
  2467. end;
  2468. end;
  2469. end;
  2470. BSUBEXP: begin //###0.936
  2471. sestart := startp [ord (opnd^)];
  2472. if sestart = nil
  2473. then EXIT;
  2474. seend := endp [ord (opnd^)];
  2475. if seend = nil
  2476. then EXIT;
  2477. REPEAT
  2478. opnd := sestart;
  2479. while opnd < seend do begin
  2480. if (scan >= fInputEnd) or (scan^ <> opnd^)
  2481. then EXIT;
  2482. inc (scan);
  2483. inc (opnd);
  2484. end;
  2485. inc (Result);
  2486. reginput := scan;
  2487. UNTIL Result >= AMax;
  2488. end;
  2489. BSUBEXPCI: begin //###0.936
  2490. sestart := startp [ord (opnd^)];
  2491. if sestart = nil
  2492. then EXIT;
  2493. seend := endp [ord (opnd^)];
  2494. if seend = nil
  2495. then EXIT;
  2496. REPEAT
  2497. opnd := sestart;
  2498. while opnd < seend do begin
  2499. if (scan >= fInputEnd) or
  2500. ((scan^ <> opnd^) and (scan^ <> InvertCase (opnd^)))
  2501. then EXIT;
  2502. inc (scan);
  2503. inc (opnd);
  2504. end;
  2505. inc (Result);
  2506. reginput := scan;
  2507. UNTIL Result >= AMax;
  2508. end;
  2509. ANYDIGIT:
  2510. while (Result < TheMax) and
  2511. (scan^ >= '0') and (scan^ <= '9') do begin
  2512. inc (Result);
  2513. inc (scan);
  2514. end;
  2515. NOTDIGIT:
  2516. while (Result < TheMax) and
  2517. ((scan^ < '0') or (scan^ > '9')) do begin
  2518. inc (Result);
  2519. inc (scan);
  2520. end;
  2521. {$IFNDEF UseSetOfChar} //###0.929
  2522. ANYLETTER:
  2523. while (Result < TheMax) and
  2524. (Pos (scan^, fWordChars) > 0) //###0.940
  2525. { ((scan^ >= 'a') and (scan^ <= 'z') !! I've forgotten (>='0') and (<='9')
  2526. or (scan^ >= 'A') and (scan^ <= 'Z') or (scan^ = '_'))} do begin
  2527. inc (Result);
  2528. inc (scan);
  2529. end;
  2530. NOTLETTER:
  2531. while (Result < TheMax) and
  2532. (Pos (scan^, fWordChars) <= 0) //###0.940
  2533. { not ((scan^ >= 'a') and (scan^ <= 'z') !! I've forgotten (>='0') and (<='9')
  2534. or (scan^ >= 'A') and (scan^ <= 'Z')
  2535. or (scan^ = '_'))} do begin
  2536. inc (Result);
  2537. inc (scan);
  2538. end;
  2539. ANYSPACE:
  2540. while (Result < TheMax) and
  2541. IsSpaceChar(scan) do begin
  2542. inc (Result);
  2543. inc (scan);
  2544. end;
  2545. NOTSPACE:
  2546. while (Result < TheMax) and
  2547. Not IsSpaceChar(scan) do begin
  2548. inc (Result);
  2549. inc (scan);
  2550. end;
  2551. {$ENDIF}
  2552. ANYOFTINYSET: begin
  2553. while (Result < TheMax) and //!!!TinySet
  2554. ((scan^ = opnd^) or (scan^ = (opnd + 1)^)
  2555. or (scan^ = (opnd + 2)^)) do begin
  2556. inc (Result);
  2557. inc (scan);
  2558. end;
  2559. end;
  2560. ANYBUTTINYSET: begin
  2561. while (Result < TheMax) and //!!!TinySet
  2562. (scan^ <> opnd^) and (scan^ <> (opnd + 1)^)
  2563. and (scan^ <> (opnd + 2)^) do begin
  2564. inc (Result);
  2565. inc (scan);
  2566. end;
  2567. end;
  2568. {$IFDEF UseSetOfChar} //###0.929
  2569. ANYOFFULLSET: begin
  2570. while (Result < TheMax) and
  2571. (scan^ in PSetOfREChar (opnd)^) do begin
  2572. inc (Result);
  2573. inc (scan);
  2574. end;
  2575. end;
  2576. {$ELSE}
  2577. ANYOF:
  2578. while (Result < TheMax) and
  2579. (StrScan (opnd, scan^) <> nil) do begin
  2580. inc (Result);
  2581. inc (scan);
  2582. end;
  2583. ANYBUT:
  2584. while (Result < TheMax) and
  2585. (StrScan (opnd, scan^) = nil) do begin
  2586. inc (Result);
  2587. inc (scan);
  2588. end;
  2589. ANYOFCI:
  2590. while (Result < TheMax) and (StrScanCI (opnd, scan^) <> nil) do begin
  2591. inc (Result);
  2592. inc (scan);
  2593. end;
  2594. ANYBUTCI:
  2595. while (Result < TheMax) and (StrScanCI (opnd, scan^) = nil) do begin
  2596. inc (Result);
  2597. inc (scan);
  2598. end;
  2599. {$ENDIF}
  2600. else begin // Oh dear. Called inappropriately.
  2601. Result := 0; // Best compromise.
  2602. Error (reeRegRepeatCalledInappropriately);
  2603. EXIT;
  2604. end;
  2605. end; { of case}
  2606. reginput := scan;
  2607. end; { of function TRegExpr.regrepeat
  2608. --------------------------------------------------------------}
  2609. function TRegExpr.regnext (p : PRegExprChar) : PRegExprChar;
  2610. // dig the "next" pointer out of a node
  2611. var offset : TRENextOff;
  2612. begin
  2613. if p = @regdummy then begin
  2614. Result := nil;
  2615. EXIT;
  2616. end;
  2617. offset := PRENextOff (AlignToPtr(p + REOpSz))^; //###0.933 inlined NEXT
  2618. if offset = 0
  2619. then Result := nil
  2620. else Result := p + offset;
  2621. end; { of function TRegExpr.regnext
  2622. --------------------------------------------------------------}
  2623. function TRegExpr.MatchPrim (prog : PRegExprChar) : boolean;
  2624. // recursively matching routine
  2625. // Conceptually the strategy is simple: check to see whether the current
  2626. // node matches, call self recursively to see whether the rest matches,
  2627. // and then act accordingly. In practice we make some effort to avoid
  2628. // recursion, in particular by going through "ordinary" nodes (that don't
  2629. // need to know whether the rest of the match failed) by a loop instead of
  2630. // by recursion.
  2631. Type
  2632. TLoopStack = array [1 .. LoopStackMax] of integer;
  2633. var
  2634. scan : PRegExprChar; // Current node.
  2635. next : PRegExprChar; // Next node.
  2636. len : PtrInt;
  2637. opnd : PRegExprChar;
  2638. no : PtrInt;
  2639. save : PRegExprChar;
  2640. nextch : REChar;
  2641. BracesMin, BracesMax : PtrInt; // we use integer instead of TREBracesArg for better support */+
  2642. {$IFDEF ComplexBraces}
  2643. SavedLoopStack : TloopStack; // :(( very bad for recursion
  2644. SavedLoopStackIdx : integer; //###0.925
  2645. {$ENDIF}
  2646. begin
  2647. Result := false;
  2648. scan := prog;
  2649. SavedLoopStack:=Default(TLoopStack);
  2650. while scan <> nil do begin
  2651. len := PRENextOff (AlignToPtr(scan + 1))^; //###0.932 inlined regnext
  2652. if len = 0
  2653. then next := nil
  2654. else next := scan + len;
  2655. case scan^ of
  2656. NOTBOUND, //###0.943 //!!! think about UseSetOfChar !!!
  2657. BOUND:
  2658. if (scan^ = BOUND)
  2659. xor (
  2660. ((reginput = fInputStart) or (Pos ((reginput - 1)^, fWordChars) <= 0))
  2661. and (reginput^ <> #0) and (Pos (reginput^, fWordChars) > 0)
  2662. or
  2663. (reginput <> fInputStart) and (Pos ((reginput - 1)^, fWordChars) > 0)
  2664. and ((reginput^ = #0) or (Pos (reginput^, fWordChars) <= 0)))
  2665. then EXIT;
  2666. BOL: if reginput <> fInputStart
  2667. then EXIT;
  2668. EOL: if reginput^ <> #0
  2669. then EXIT;
  2670. BOLML: if reginput > fInputStart then begin
  2671. nextch := (reginput - 1)^;
  2672. if (nextch <> fLinePairedSeparatorTail)
  2673. or ((reginput - 1) <= fInputStart)
  2674. or ((reginput - 2)^ <> fLinePairedSeparatorHead)
  2675. then begin
  2676. if (nextch = fLinePairedSeparatorHead)
  2677. and (reginput^ = fLinePairedSeparatorTail)
  2678. then EXIT; // don't stop between paired separator
  2679. if
  2680. {$IFNDEF UniCode}
  2681. not (nextch in fLineSeparatorsSet)
  2682. {$ELSE}
  2683. (pos (nextch, fLineSeparators) <= 0)
  2684. {$ENDIF}
  2685. then EXIT;
  2686. end;
  2687. end;
  2688. EOLML: if reginput^ <> #0 then begin
  2689. nextch := reginput^;
  2690. if (nextch <> fLinePairedSeparatorHead)
  2691. or ((reginput + 1)^ <> fLinePairedSeparatorTail)
  2692. then begin
  2693. if (nextch = fLinePairedSeparatorTail)
  2694. and (reginput > fInputStart)
  2695. and ((reginput - 1)^ = fLinePairedSeparatorHead)
  2696. then EXIT; // don't stop between paired separator
  2697. if
  2698. {$IFNDEF UniCode}
  2699. not (nextch in fLineSeparatorsSet)
  2700. {$ELSE}
  2701. (pos (nextch, fLineSeparators) <= 0)
  2702. {$ENDIF}
  2703. then EXIT;
  2704. end;
  2705. end;
  2706. ANY: begin
  2707. if reginput^ = #0
  2708. then EXIT;
  2709. inc (reginput);
  2710. end;
  2711. ANYML: begin //###0.941
  2712. if (reginput^ = #0)
  2713. or ((reginput^ = fLinePairedSeparatorHead)
  2714. and ((reginput + 1)^ = fLinePairedSeparatorTail))
  2715. or {$IFNDEF UniCode} (reginput^ in fLineSeparatorsSet)
  2716. {$ELSE} (pos (reginput^, fLineSeparators) > 0) {$ENDIF}
  2717. then EXIT;
  2718. inc (reginput);
  2719. end;
  2720. ANYDIGIT: begin
  2721. if (reginput^ = #0) or (reginput^ < '0') or (reginput^ > '9')
  2722. then EXIT;
  2723. inc (reginput);
  2724. end;
  2725. NOTDIGIT: begin
  2726. if (reginput^ = #0) or ((reginput^ >= '0') and (reginput^ <= '9'))
  2727. then EXIT;
  2728. inc (reginput);
  2729. end;
  2730. {$IFNDEF UseSetOfChar} //###0.929
  2731. ANYLETTER: begin
  2732. if (reginput^ = #0) or (Pos (reginput^, fWordChars) <= 0) //###0.943
  2733. then EXIT;
  2734. inc (reginput);
  2735. end;
  2736. NOTLETTER: begin
  2737. if (reginput^ = #0) or (Pos (reginput^, fWordChars) > 0) //###0.943
  2738. then EXIT;
  2739. inc (reginput);
  2740. end;
  2741. ANYSPACE: begin
  2742. if (reginput^ = #0) or not IsSpaceChar(reginput) //###0.943
  2743. then EXIT;
  2744. inc (reginput);
  2745. end;
  2746. NOTSPACE: begin
  2747. if (reginput^ = #0) or IsSpaceChar(reginput) //###0.943
  2748. then EXIT;
  2749. inc (reginput);
  2750. end;
  2751. {$ENDIF}
  2752. EXACTLYCI: begin
  2753. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  2754. // Inline the first character, for speed.
  2755. if (opnd^ <> reginput^)
  2756. and (InvertCase (opnd^) <> reginput^)
  2757. then EXIT;
  2758. len := strlen (opnd);
  2759. //###0.929 begin
  2760. no := len;
  2761. save := reginput;
  2762. while no > 1 do begin
  2763. inc (save);
  2764. inc (opnd);
  2765. if (opnd^ <> save^)
  2766. and (InvertCase (opnd^) <> save^)
  2767. then EXIT;
  2768. dec (no);
  2769. end;
  2770. //###0.929 end
  2771. inc (reginput, len);
  2772. end;
  2773. EXACTLY: begin
  2774. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  2775. // Inline the first character, for speed.
  2776. if opnd^ <> reginput^
  2777. then EXIT;
  2778. len := strlen (opnd);
  2779. //###0.929 begin
  2780. no := len;
  2781. save := reginput;
  2782. while no > 1 do begin
  2783. inc (save);
  2784. inc (opnd);
  2785. if opnd^ <> save^
  2786. then EXIT;
  2787. dec (no);
  2788. end;
  2789. //###0.929 end
  2790. inc (reginput, len);
  2791. end;
  2792. BSUBEXP: begin //###0.936
  2793. no := ord ((scan + REOpSz + RENextOffSz)^);
  2794. if startp [no] = nil
  2795. then EXIT;
  2796. if endp [no] = nil
  2797. then EXIT;
  2798. save := reginput;
  2799. opnd := startp [no];
  2800. while opnd < endp [no] do begin
  2801. if (save >= fInputEnd) or (save^ <> opnd^)
  2802. then EXIT;
  2803. inc (save);
  2804. inc (opnd);
  2805. end;
  2806. reginput := save;
  2807. end;
  2808. BSUBEXPCI: begin //###0.936
  2809. no := ord ((scan + REOpSz + RENextOffSz)^);
  2810. if startp [no] = nil
  2811. then EXIT;
  2812. if endp [no] = nil
  2813. then EXIT;
  2814. save := reginput;
  2815. opnd := startp [no];
  2816. while opnd < endp [no] do begin
  2817. if (save >= fInputEnd) or
  2818. ((save^ <> opnd^) and (save^ <> InvertCase (opnd^)))
  2819. then EXIT;
  2820. inc (save);
  2821. inc (opnd);
  2822. end;
  2823. reginput := save;
  2824. end;
  2825. ANYOFTINYSET: begin
  2826. if (reginput^ = #0) or //!!!TinySet
  2827. ((reginput^ <> (scan + REOpSz + RENextOffSz)^)
  2828. and (reginput^ <> (scan + REOpSz + RENextOffSz + 1)^)
  2829. and (reginput^ <> (scan + REOpSz + RENextOffSz + 2)^))
  2830. then EXIT;
  2831. inc (reginput);
  2832. end;
  2833. ANYBUTTINYSET: begin
  2834. if (reginput^ = #0) or //!!!TinySet
  2835. (reginput^ = (scan + REOpSz + RENextOffSz)^)
  2836. or (reginput^ = (scan + REOpSz + RENextOffSz + 1)^)
  2837. or (reginput^ = (scan + REOpSz + RENextOffSz + 2)^)
  2838. then EXIT;
  2839. inc (reginput);
  2840. end;
  2841. {$IFDEF UseSetOfChar} //###0.929
  2842. ANYOFFULLSET: begin
  2843. if (reginput^ = #0)
  2844. or not (reginput^ in PSetOfREChar (scan + REOpSz + RENextOffSz)^)
  2845. then EXIT;
  2846. inc (reginput);
  2847. end;
  2848. {$ELSE}
  2849. ANYOF: begin
  2850. if (reginput^ = #0) or (StrScan (scan + REOpSz + RENextOffSz, reginput^) = nil)
  2851. then EXIT;
  2852. inc (reginput);
  2853. end;
  2854. ANYBUT: begin
  2855. if (reginput^ = #0) or (StrScan (scan + REOpSz + RENextOffSz, reginput^) <> nil)
  2856. then EXIT;
  2857. inc (reginput);
  2858. end;
  2859. ANYOFCI: begin
  2860. if (reginput^ = #0) or (StrScanCI (scan + REOpSz + RENextOffSz, reginput^) = nil)
  2861. then EXIT;
  2862. inc (reginput);
  2863. end;
  2864. ANYBUTCI: begin
  2865. if (reginput^ = #0) or (StrScanCI (scan + REOpSz + RENextOffSz, reginput^) <> nil)
  2866. then EXIT;
  2867. inc (reginput);
  2868. end;
  2869. {$ENDIF}
  2870. NOTHING: ;
  2871. COMMENT: ;
  2872. BACK: ;
  2873. Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1) : begin //###0.929
  2874. no := ord (scan^) - ord (OPEN);
  2875. // save := reginput;
  2876. save := startp [no]; //###0.936
  2877. startp [no] := reginput; //###0.936
  2878. Result := MatchPrim (next);
  2879. if not Result //###0.936
  2880. then startp [no] := save;
  2881. // if Result and (startp [no] = nil)
  2882. // then startp [no] := save;
  2883. // Don't set startp if some later invocation of the same
  2884. // parentheses already has.
  2885. EXIT;
  2886. end;
  2887. Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): begin //###0.929
  2888. no := ord (scan^) - ord (CLOSE);
  2889. // save := reginput;
  2890. save := endp [no]; //###0.936
  2891. endp [no] := reginput; //###0.936
  2892. Result := MatchPrim (next);
  2893. if not Result //###0.936
  2894. then endp [no] := save;
  2895. // if Result and (endp [no] = nil)
  2896. // then endp [no] := save;
  2897. // Don't set endp if some later invocation of the same
  2898. // parentheses already has.
  2899. EXIT;
  2900. end;
  2901. BRANCH: begin
  2902. if (next^ <> BRANCH) // No choice.
  2903. then next := scan + REOpSz + RENextOffSz // Avoid recursion
  2904. else begin
  2905. REPEAT
  2906. save := reginput;
  2907. Result := MatchPrim (scan + REOpSz + RENextOffSz);
  2908. if Result
  2909. then EXIT;
  2910. reginput := save;
  2911. scan := regnext (scan);
  2912. UNTIL (scan = nil) or (scan^ <> BRANCH);
  2913. EXIT;
  2914. end;
  2915. end;
  2916. {$IFDEF ComplexBraces}
  2917. LOOPENTRY: begin //###0.925
  2918. no := LoopStackIdx;
  2919. inc (LoopStackIdx);
  2920. if LoopStackIdx > LoopStackMax then begin
  2921. Error (reeLoopStackExceeded);
  2922. EXIT;
  2923. end;
  2924. save := reginput;
  2925. LoopStack [LoopStackIdx] := 0; // init loop counter
  2926. Result := MatchPrim (next); // execute LOOP
  2927. LoopStackIdx := no; // cleanup
  2928. if Result
  2929. then EXIT;
  2930. reginput := save;
  2931. EXIT;
  2932. end;
  2933. LOOP, LOOPNG: begin //###0.940
  2934. if LoopStackIdx <= 0 then begin
  2935. Error (reeLoopWithoutEntry);
  2936. EXIT;
  2937. end;
  2938. opnd := scan + PRENextOff (AlignToPtr(scan + REOpSz + RENextOffSz + 2 * REBracesArgSz))^;
  2939. BracesMin := PREBracesArg (AlignToInt(scan + REOpSz + RENextOffSz))^;
  2940. BracesMax := PREBracesArg (AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  2941. save := reginput;
  2942. if LoopStack [LoopStackIdx] >= BracesMin then begin // Min alredy matched - we can work
  2943. if scan^ = LOOP then begin
  2944. // greedy way - first try to max deep of greed ;)
  2945. if LoopStack [LoopStackIdx] < BracesMax then begin
  2946. inc (LoopStack [LoopStackIdx]);
  2947. no := LoopStackIdx;
  2948. Result := MatchPrim (opnd);
  2949. LoopStackIdx := no;
  2950. if Result
  2951. then EXIT;
  2952. reginput := save;
  2953. end;
  2954. dec (LoopStackIdx); // Fail. May be we are too greedy? ;)
  2955. Result := MatchPrim (next);
  2956. if not Result
  2957. then reginput := save;
  2958. EXIT;
  2959. end
  2960. else begin
  2961. // non-greedy - try just now
  2962. Result := MatchPrim (next);
  2963. if Result
  2964. then EXIT
  2965. else reginput := save; // failed - move next and try again
  2966. if LoopStack [LoopStackIdx] < BracesMax then begin
  2967. inc (LoopStack [LoopStackIdx]);
  2968. no := LoopStackIdx;
  2969. Result := MatchPrim (opnd);
  2970. LoopStackIdx := no;
  2971. if Result
  2972. then EXIT;
  2973. reginput := save;
  2974. end;
  2975. dec (LoopStackIdx); // Failed - back up
  2976. EXIT;
  2977. end
  2978. end
  2979. else begin // first match a min_cnt times
  2980. inc (LoopStack [LoopStackIdx]);
  2981. no := LoopStackIdx;
  2982. Result := MatchPrim (opnd);
  2983. LoopStackIdx := no;
  2984. if Result
  2985. then EXIT;
  2986. dec (LoopStack [LoopStackIdx]);
  2987. reginput := save;
  2988. EXIT;
  2989. end;
  2990. end;
  2991. {$ENDIF}
  2992. STAR, PLUS, BRACES, STARNG, PLUSNG, BRACESNG: begin
  2993. // Lookahead to avoid useless match attempts when we know
  2994. // what character comes next.
  2995. nextch := #0;
  2996. if next^ = EXACTLY
  2997. then nextch := (next + REOpSz + RENextOffSz)^;
  2998. BracesMax := MaxInt; // infinite loop for * and + //###0.92
  2999. if (scan^ = STAR) or (scan^ = STARNG)
  3000. then BracesMin := 0 // STAR
  3001. else if (scan^ = PLUS) or (scan^ = PLUSNG)
  3002. then BracesMin := 1 // PLUS
  3003. else begin // BRACES
  3004. BracesMin := PREBracesArg (AlignToPtr(scan + REOpSz + RENextOffSz))^;
  3005. BracesMax := PREBracesArg (AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  3006. end;
  3007. save := reginput;
  3008. opnd := scan + REOpSz + RENextOffSz;
  3009. if (scan^ = BRACES) or (scan^ = BRACESNG)
  3010. then inc (opnd, 2 * REBracesArgSz);
  3011. if (scan^ = PLUSNG) or (scan^ = STARNG) or (scan^ = BRACESNG) then begin
  3012. // non-greedy mode
  3013. BracesMax := regrepeat (opnd, BracesMax); // don't repeat more than BracesMax
  3014. // Now we know real Max limit to move forward (for recursion 'back up')
  3015. // In some cases it can be faster to check only Min positions first,
  3016. // but after that we have to check every position separtely instead
  3017. // of fast scannig in loop.
  3018. no := BracesMin;
  3019. while no <= BracesMax do begin
  3020. reginput := save + no;
  3021. // If it could work, try it.
  3022. if (nextch = #0) or (reginput^ = nextch) then begin
  3023. {$IFDEF ComplexBraces}
  3024. System.Move (LoopStack, SavedLoopStack, SizeOf (LoopStack)); //###0.925
  3025. SavedLoopStackIdx := LoopStackIdx;
  3026. {$ENDIF}
  3027. if MatchPrim (next) then begin
  3028. Result := true;
  3029. EXIT;
  3030. end;
  3031. {$IFDEF ComplexBraces}
  3032. System.Move (SavedLoopStack, LoopStack, SizeOf (LoopStack));
  3033. LoopStackIdx := SavedLoopStackIdx;
  3034. {$ENDIF}
  3035. end;
  3036. inc (no); // Couldn't or didn't - move forward.
  3037. end; { of while}
  3038. EXIT;
  3039. end
  3040. else begin // greedy mode
  3041. no := regrepeat (opnd, BracesMax); // don't repeat more than max_cnt
  3042. while no >= BracesMin do begin
  3043. // If it could work, try it.
  3044. if (nextch = #0) or (reginput^ = nextch) then begin
  3045. {$IFDEF ComplexBraces}
  3046. System.Move (LoopStack, SavedLoopStack, SizeOf (LoopStack)); //###0.925
  3047. SavedLoopStackIdx := LoopStackIdx;
  3048. {$ENDIF}
  3049. if MatchPrim (next) then begin
  3050. Result := true;
  3051. EXIT;
  3052. end;
  3053. {$IFDEF ComplexBraces}
  3054. System.Move (SavedLoopStack, LoopStack, SizeOf (LoopStack));
  3055. LoopStackIdx := SavedLoopStackIdx;
  3056. {$ENDIF}
  3057. end;
  3058. dec (no); // Couldn't or didn't - back up.
  3059. reginput := save + no;
  3060. end; { of while}
  3061. EXIT;
  3062. end;
  3063. end;
  3064. EEND: begin
  3065. Result := true; // Success!
  3066. EXIT;
  3067. end;
  3068. else begin
  3069. Error (reeMatchPrimMemoryCorruption);
  3070. EXIT;
  3071. end;
  3072. end; { of case scan^}
  3073. scan := next;
  3074. end; { of while scan <> nil}
  3075. // We get here only if there's trouble -- normally "case EEND" is the
  3076. // terminating point.
  3077. Error (reeMatchPrimCorruptedPointers);
  3078. end; { of function TRegExpr.MatchPrim
  3079. --------------------------------------------------------------}
  3080. {$IFDEF UseFirstCharSet} //###0.929
  3081. procedure TRegExpr.FillFirstCharSet (prog : PRegExprChar);
  3082. var
  3083. scan : PRegExprChar; // Current node.
  3084. next : PRegExprChar; // Next node.
  3085. opnd : PRegExprChar;
  3086. min_cnt : integer;
  3087. begin
  3088. scan := prog;
  3089. while scan <> nil do begin
  3090. next := regnext (scan);
  3091. case PREOp (scan)^ of
  3092. BSUBEXP, BSUBEXPCI: begin //###0.938
  3093. FirstCharSet := [#0 .. #255]; // :((( we cannot
  3094. // optimize r.e. if it starts with back reference
  3095. EXIT;
  3096. end;
  3097. BOL, BOLML: ; // EXIT; //###0.937
  3098. EOL, EOLML: begin //###0.948 was empty in 0.947, was EXIT in 0.937
  3099. Include (FirstCharSet, #0);
  3100. if ModifierM
  3101. then begin
  3102. opnd := PRegExprChar (LineSeparators);
  3103. while opnd^ <> #0 do begin
  3104. Include (FirstCharSet, opnd^);
  3105. inc (opnd);
  3106. end;
  3107. end;
  3108. EXIT;
  3109. end;
  3110. BOUND, NOTBOUND: ; //###0.943 ?!!
  3111. ANY, ANYML: begin // we can better define ANYML !!!
  3112. FirstCharSet := [#0 .. #255]; //###0.930
  3113. EXIT;
  3114. end;
  3115. ANYDIGIT: begin
  3116. FirstCharSet := FirstCharSet + ['0' .. '9'];
  3117. EXIT;
  3118. end;
  3119. NOTDIGIT: begin
  3120. FirstCharSet := FirstCharSet + ([#0 .. #255] - ['0' .. '9']); //###0.948 FirstCharSet was forgotten
  3121. EXIT;
  3122. end;
  3123. EXACTLYCI: begin
  3124. Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);
  3125. Include (FirstCharSet, InvertCase ((scan + REOpSz + RENextOffSz)^));
  3126. EXIT;
  3127. end;
  3128. EXACTLY: begin
  3129. Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);
  3130. EXIT;
  3131. end;
  3132. ANYOFFULLSET: begin
  3133. FirstCharSet := FirstCharSet + PSetOfREChar (scan + REOpSz + RENextOffSz)^;
  3134. EXIT;
  3135. end;
  3136. ANYOFTINYSET: begin
  3137. //!!!TinySet
  3138. Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);
  3139. Include (FirstCharSet, (scan + REOpSz + RENextOffSz + 1)^);
  3140. Include (FirstCharSet, (scan + REOpSz + RENextOffSz + 2)^);
  3141. // ... // up to TinySetLen
  3142. EXIT;
  3143. end;
  3144. ANYBUTTINYSET: begin
  3145. //!!!TinySet
  3146. FirstCharSet := FirstCharSet + ([#0 .. #255] - [ //###0.948 FirstCharSet was forgotten
  3147. (scan + REOpSz + RENextOffSz)^,
  3148. (scan + REOpSz + RENextOffSz + 1)^,
  3149. (scan + REOpSz + RENextOffSz + 2)^]);
  3150. // ... // up to TinySetLen
  3151. EXIT;
  3152. end;
  3153. NOTHING: ;
  3154. COMMENT: ;
  3155. BACK: ;
  3156. Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1) : begin //###0.929
  3157. FillFirstCharSet (next);
  3158. EXIT;
  3159. end;
  3160. Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): begin //###0.929
  3161. FillFirstCharSet (next);
  3162. EXIT;
  3163. end;
  3164. BRANCH: begin
  3165. if (PREOp (next)^ <> BRANCH) // No choice.
  3166. then next := scan + REOpSz + RENextOffSz // Avoid recursion.
  3167. else begin
  3168. REPEAT
  3169. FillFirstCharSet (scan + REOpSz + RENextOffSz);
  3170. scan := regnext (scan);
  3171. UNTIL (scan = nil) or (PREOp (scan)^ <> BRANCH);
  3172. EXIT;
  3173. end;
  3174. end;
  3175. {$IFDEF ComplexBraces}
  3176. LOOPENTRY: begin //###0.925
  3177. // LoopStack [LoopStackIdx] := 0; //###0.940 line removed
  3178. FillFirstCharSet (next); // execute LOOP
  3179. EXIT;
  3180. end;
  3181. LOOP, LOOPNG: begin //###0.940
  3182. opnd := scan + PRENextOff (AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz * 2))^;
  3183. min_cnt := PREBracesArg (AlignToPtr(scan + REOpSz + RENextOffSz))^;
  3184. FillFirstCharSet (opnd);
  3185. if min_cnt = 0
  3186. then FillFirstCharSet (next);
  3187. EXIT;
  3188. end;
  3189. {$ENDIF}
  3190. STAR, STARNG: //###0.940
  3191. FillFirstCharSet (scan + REOpSz + RENextOffSz);
  3192. PLUS, PLUSNG: begin //###0.940
  3193. FillFirstCharSet (scan + REOpSz + RENextOffSz);
  3194. EXIT;
  3195. end;
  3196. BRACES, BRACESNG: begin //###0.940
  3197. opnd := scan + REOpSz + RENextOffSz + REBracesArgSz * 2;
  3198. min_cnt := PREBracesArg (AlignToPtr(scan + REOpSz + RENextOffSz))^; // BRACES
  3199. FillFirstCharSet (opnd);
  3200. if min_cnt > 0
  3201. then EXIT;
  3202. end;
  3203. EEND: begin
  3204. FirstCharSet := [#0 .. #255]; //###0.948
  3205. EXIT;
  3206. end;
  3207. else begin
  3208. Error (reeMatchPrimMemoryCorruption);
  3209. EXIT;
  3210. end;
  3211. end; { of case scan^}
  3212. scan := next;
  3213. end; { of while scan <> nil}
  3214. end; { of procedure FillFirstCharSet
  3215. --------------------------------------------------------------}
  3216. {$ENDIF}
  3217. function TRegExpr.Exec (const AInputString : RegExprString) : boolean;
  3218. begin
  3219. InputString := AInputString;
  3220. Result := ExecPrim (1);
  3221. end; { of function TRegExpr.Exec
  3222. --------------------------------------------------------------}
  3223. {$IFDEF OverMeth}
  3224. {$IFNDEF FPC}
  3225. function TRegExpr.Exec : boolean;
  3226. begin
  3227. Result := ExecPrim (1);
  3228. end; { of function TRegExpr.Exec
  3229. --------------------------------------------------------------}
  3230. {$ENDIF}
  3231. function TRegExpr.Exec (AOffset: PtrInt) : boolean;
  3232. begin
  3233. Result := ExecPrim (AOffset);
  3234. end; { of function TRegExpr.Exec
  3235. --------------------------------------------------------------}
  3236. {$ENDIF}
  3237. function TRegExpr.ExecPos (AOffset: PtrInt {$IFDEF DefParam}= 1{$ENDIF}) : boolean;
  3238. begin
  3239. Result := ExecPrim (AOffset);
  3240. end; { of function TRegExpr.ExecPos
  3241. --------------------------------------------------------------}
  3242. function TRegExpr.ExecPrim (AOffset: PtrInt) : boolean;
  3243. procedure ClearMatchs;
  3244. // Clears matchs array
  3245. var i : integer;
  3246. begin
  3247. for i := 0 to NSUBEXP - 1 do begin
  3248. startp [i] := nil;
  3249. endp [i] := nil;
  3250. end;
  3251. end; { of procedure ClearMatchs;
  3252. ..............................................................}
  3253. function RegMatch (str : PRegExprChar) : boolean;
  3254. // try match at specific point
  3255. begin
  3256. //###0.949 removed clearing of start\endp
  3257. reginput := str;
  3258. Result := MatchPrim (programm + REOpSz);
  3259. if Result then begin
  3260. startp [0] := str;
  3261. endp [0] := reginput;
  3262. end;
  3263. end; { of function RegMatch
  3264. ..............................................................}
  3265. var
  3266. s : PRegExprChar;
  3267. StartPtr: PRegExprChar;
  3268. InputLen : PtrInt;
  3269. begin
  3270. Result := false; // Be paranoid...
  3271. ClearMatchs; //###0.949
  3272. // ensure that Match cleared either if optimization tricks or some error
  3273. // will lead to leaving ExecPrim without actual search. That is
  3274. // importent for ExecNext logic and so on.
  3275. if not IsProgrammOk //###0.929
  3276. then EXIT;
  3277. // Check InputString presence
  3278. if not Assigned (fInputString) then begin
  3279. Error (reeNoInputStringSpecified);
  3280. EXIT;
  3281. end;
  3282. InputLen := length (fInputString);
  3283. //Check that the start position is not negative
  3284. if AOffset < 1 then begin
  3285. Error (reeOffsetMustBeGreaterThen0);
  3286. EXIT;
  3287. end;
  3288. // Check that the start position is not longer than the line
  3289. // If so then exit with nothing found
  3290. if AOffset > (InputLen + 1) // for matching empty string after last char.
  3291. then EXIT;
  3292. StartPtr := fInputString + AOffset - 1;
  3293. // If there is a "must appear" string, look for it.
  3294. if regmust <> nil then begin
  3295. s := StartPtr;
  3296. REPEAT
  3297. s := StrScan (s, regmust [0]);
  3298. if s <> nil then begin
  3299. if StrLComp (s, regmust, regmlen) = 0
  3300. then BREAK; // Found it.
  3301. inc (s);
  3302. end;
  3303. UNTIL s = nil;
  3304. if s = nil // Not present.
  3305. then EXIT;
  3306. end;
  3307. // Mark beginning of line for ^ .
  3308. fInputStart := fInputString;
  3309. // Pointer to end of input stream - for
  3310. // pascal-style string processing (may include #0)
  3311. fInputEnd := fInputString + InputLen;
  3312. {$IFDEF ComplexBraces}
  3313. // no loops started
  3314. LoopStackIdx := 0; //###0.925
  3315. {$ENDIF}
  3316. // Simplest case: anchored match need be tried only once.
  3317. if reganch <> #0 then begin
  3318. Result := RegMatch (StartPtr);
  3319. EXIT;
  3320. end;
  3321. // Messy cases: unanchored match.
  3322. s := StartPtr;
  3323. if regstart <> #0 then // We know what char it must start with.
  3324. REPEAT
  3325. s := StrScan (s, regstart);
  3326. if s <> nil then begin
  3327. Result := RegMatch (s);
  3328. if Result
  3329. then EXIT
  3330. else ClearMatchs; //###0.949
  3331. inc (s);
  3332. end;
  3333. UNTIL s = nil
  3334. else begin // We don't - general case.
  3335. repeat //###0.948
  3336. {$IFDEF UseFirstCharSet}
  3337. if s^ in FirstCharSet
  3338. then Result := RegMatch (s);
  3339. {$ELSE}
  3340. Result := RegMatch (s);
  3341. {$ENDIF}
  3342. if Result or (s^ = #0) // Exit on a match or after testing the end-of-string.
  3343. then EXIT
  3344. else ClearMatchs; //###0.949
  3345. inc (s);
  3346. until false;
  3347. (* optimized and fixed by Martin Fuller - empty strings
  3348. were not allowed to pass through in UseFirstCharSet mode
  3349. {$IFDEF UseFirstCharSet} //###0.929
  3350. while s^ <> #0 do begin
  3351. if s^ in FirstCharSet
  3352. then Result := RegMatch (s);
  3353. if Result
  3354. then EXIT;
  3355. inc (s);
  3356. end;
  3357. {$ELSE}
  3358. REPEAT
  3359. Result := RegMatch (s);
  3360. if Result
  3361. then EXIT;
  3362. inc (s);
  3363. UNTIL s^ = #0;
  3364. {$ENDIF}
  3365. *)
  3366. end;
  3367. // Failure
  3368. end; { of function TRegExpr.ExecPrim
  3369. --------------------------------------------------------------}
  3370. function TRegExpr.ExecNext : boolean;
  3371. var offset : PtrInt;
  3372. begin
  3373. Result := false;
  3374. if not Assigned (startp[0]) or not Assigned (endp[0]) then begin
  3375. Error (reeExecNextWithoutExec);
  3376. EXIT;
  3377. end;
  3378. // Offset := MatchPos [0] + MatchLen [0];
  3379. // if MatchLen [0] = 0
  3380. Offset := endp [0] - fInputString + 1; //###0.929
  3381. if endp [0] = startp [0] //###0.929
  3382. then inc (Offset); // prevent infinite looping if empty string match r.e.
  3383. Result := ExecPrim (Offset);
  3384. end; { of function TRegExpr.ExecNext
  3385. --------------------------------------------------------------}
  3386. function TRegExpr.GetInputString : RegExprString;
  3387. begin
  3388. if not Assigned (fInputString) then begin
  3389. Error (reeGetInputStringWithoutInputString);
  3390. EXIT;
  3391. end;
  3392. Result := fInputString;
  3393. end; { of function TRegExpr.GetInputString
  3394. --------------------------------------------------------------}
  3395. procedure TRegExpr.SetInputString (const AInputString : RegExprString);
  3396. var
  3397. Len : PtrInt;
  3398. i : PtrInt;
  3399. begin
  3400. // clear Match* - before next Exec* call it's undefined
  3401. for i := 0 to NSUBEXP - 1 do begin
  3402. startp [i] := nil;
  3403. endp [i] := nil;
  3404. end;
  3405. // need reallocation of input string buffer ?
  3406. Len := length (AInputString);
  3407. ReAllocMem(fInputString,(Len + 1) * SizeOf (REChar));
  3408. // copy input string into buffer
  3409. if Len>0 then
  3410. System.Move(AInputString[1],fInputString^,(Len+1)* SizeOf (REChar)) // with #0
  3411. else
  3412. fInputString[0]:=#0;
  3413. {
  3414. fInputString : string;
  3415. fInputStart, fInputEnd : PRegExprChar;
  3416. SetInputString:
  3417. fInputString := AInputString;
  3418. UniqueString (fInputString);
  3419. fInputStart := PChar (fInputString);
  3420. Len := length (fInputString);
  3421. fInputEnd := PRegExprChar (integer (fInputStart) + Len); ??
  3422. !! startp/endp все равно будет опасно использовать ?
  3423. }
  3424. end; { of procedure TRegExpr.SetInputString
  3425. --------------------------------------------------------------}
  3426. procedure TRegExpr.SetLineSeparators (const AStr : RegExprString);
  3427. begin
  3428. if AStr <> fLineSeparators then begin
  3429. fLineSeparators := AStr;
  3430. InvalidateProgramm;
  3431. end;
  3432. end; { of procedure TRegExpr.SetLineSeparators
  3433. --------------------------------------------------------------}
  3434. procedure TRegExpr.SetLinePairedSeparator (const AStr : RegExprString);
  3435. begin
  3436. if length (AStr) = 2 then begin
  3437. if AStr [1] = AStr [2] then begin
  3438. // it's impossible for our 'one-point' checking to support
  3439. // two chars separator for identical chars
  3440. Error (reeBadLinePairedSeparator);
  3441. EXIT;
  3442. end;
  3443. if not fLinePairedSeparatorAssigned
  3444. or (AStr [1] <> fLinePairedSeparatorHead)
  3445. or (AStr [2] <> fLinePairedSeparatorTail) then begin
  3446. fLinePairedSeparatorAssigned := true;
  3447. fLinePairedSeparatorHead := AStr [1];
  3448. fLinePairedSeparatorTail := AStr [2];
  3449. InvalidateProgramm;
  3450. end;
  3451. end
  3452. else if length (AStr) = 0 then begin
  3453. if fLinePairedSeparatorAssigned then begin
  3454. fLinePairedSeparatorAssigned := false;
  3455. InvalidateProgramm;
  3456. end;
  3457. end
  3458. else Error (reeBadLinePairedSeparator);
  3459. end; { of procedure TRegExpr.SetLinePairedSeparator
  3460. --------------------------------------------------------------}
  3461. function TRegExpr.GetLinePairedSeparator : RegExprString;
  3462. begin
  3463. if fLinePairedSeparatorAssigned then begin
  3464. {$IFDEF UniCode}
  3465. // Here is some UniCode 'magic'
  3466. // If You do know better decision to concatenate
  3467. // two WideChars, please, let me know!
  3468. Result := fLinePairedSeparatorHead; //###0.947
  3469. Result := Result + fLinePairedSeparatorTail;
  3470. {$ELSE}
  3471. Result := fLinePairedSeparatorHead + fLinePairedSeparatorTail;
  3472. {$ENDIF}
  3473. end
  3474. else Result := '';
  3475. end; { of function TRegExpr.GetLinePairedSeparator
  3476. --------------------------------------------------------------}
  3477. function TRegExpr.Substitute (const ATemplate : RegExprString) : RegExprString;
  3478. // perform substitutions after a regexp match
  3479. // completely rewritten in 0.929
  3480. type
  3481. TSubstMode = (smodeNormal, smodeOneUpper, smodeOneLower, smodeAllUpper,
  3482. smodeAllLower);
  3483. var
  3484. TemplateLen : PtrInt;
  3485. TemplateBeg, TemplateEnd : PRegExprChar;
  3486. p, p0, p1, ResultPtr : PRegExprChar;
  3487. ResultLen : PtrInt;
  3488. n : PtrInt;
  3489. Ch : REChar;
  3490. Mode: TSubstMode;
  3491. LineEnd: String = {$ifdef UseOsLineEndOnReplace} System.LineEnding {$else} Chr(10) {$endif};
  3492. function ParseVarName (var APtr : PRegExprChar) : PtrInt;
  3493. // extract name of variable (digits, may be enclosed with
  3494. // curly braces) from APtr^, uses TemplateEnd !!!
  3495. const
  3496. Digits = ['0' .. '9'];
  3497. var
  3498. p : PRegExprChar;
  3499. Delimited : boolean;
  3500. begin
  3501. Result := 0;
  3502. p := APtr;
  3503. Delimited := (p < TemplateEnd) and (p^ = '{');
  3504. if Delimited
  3505. then inc (p); // skip left curly brace
  3506. if (p < TemplateEnd) and (p^ = '&')
  3507. then inc (p) // this is '$&' or '${&}'
  3508. else
  3509. while (p < TemplateEnd) and
  3510. {$IFDEF UniCode} //###0.935
  3511. (ord (p^) < 256) and (char (p^) in Digits)
  3512. {$ELSE}
  3513. (p^ in Digits)
  3514. {$ENDIF}
  3515. do begin
  3516. Result := Result * 10 + (ord (p^) - ord ('0')); //###0.939
  3517. inc (p);
  3518. end;
  3519. if Delimited then
  3520. if (p < TemplateEnd) and (p^ = '}')
  3521. then inc (p) // skip right curly brace
  3522. else p := APtr; // isn't properly terminated
  3523. if p = APtr
  3524. then Result := -1; // no valid digits found or no right curly brace
  3525. APtr := p;
  3526. end;
  3527. begin
  3528. // Check programm and input string
  3529. if not IsProgrammOk
  3530. then EXIT;
  3531. if not Assigned (fInputString) then begin
  3532. Error (reeNoInputStringSpecified);
  3533. EXIT;
  3534. end;
  3535. // Prepare for working
  3536. TemplateLen := length (ATemplate);
  3537. if TemplateLen = 0 then begin // prevent nil pointers
  3538. Result := '';
  3539. EXIT;
  3540. end;
  3541. TemplateBeg := pointer (ATemplate);
  3542. TemplateEnd := TemplateBeg + TemplateLen;
  3543. // Count result length for speed optimization.
  3544. ResultLen := 0;
  3545. p := TemplateBeg;
  3546. while p < TemplateEnd do begin
  3547. Ch := p^;
  3548. inc (p);
  3549. if Ch = '$'
  3550. then n := ParseVarName (p)
  3551. else n := -1;
  3552. if n >= 0 then begin
  3553. if (n < NSUBEXP) and Assigned (startp [n]) and Assigned (endp [n])
  3554. then inc (ResultLen, endp [n] - startp [n]);
  3555. end
  3556. else begin
  3557. if (Ch = EscChar) and (p < TemplateEnd) then begin // quoted or special char followed
  3558. Ch := p^;
  3559. inc (p);
  3560. case Ch of
  3561. 'n' : inc(ResultLen, Length(LineEnd));
  3562. 'u', 'l', 'U', 'L': {nothing};
  3563. else inc(ResultLen);
  3564. end;
  3565. end
  3566. else
  3567. inc(ResultLen);
  3568. end;
  3569. end;
  3570. // Get memory. We do it once and it significant speed up work !
  3571. if ResultLen = 0 then begin
  3572. Result := '';
  3573. EXIT;
  3574. end;
  3575. //SetString (Result, nil, ResultLen);
  3576. SetLength(Result,ResultLen);
  3577. // Fill Result
  3578. ResultPtr := pointer (Result);
  3579. p := TemplateBeg;
  3580. Mode := smodeNormal;
  3581. while p < TemplateEnd do begin
  3582. Ch := p^;
  3583. p0 := p;
  3584. inc (p);
  3585. p1 := p;
  3586. if Ch = '$'
  3587. then n := ParseVarName (p)
  3588. else n := -1;
  3589. if (n >= 0) then begin
  3590. p0 := startp[n];
  3591. p1 := endp[n];
  3592. if (n >= NSUBEXP) or not Assigned (p0) or not Assigned (endp [n]) then
  3593. p1 := p0; // empty
  3594. end
  3595. else begin
  3596. if (Ch = EscChar) and (p < TemplateEnd) then begin // quoted or special char followed
  3597. Ch := p^;
  3598. inc (p);
  3599. case Ch of
  3600. 'n' : begin
  3601. p0 := @LineEnd[1];
  3602. p1 := p0 + Length(LineEnd);
  3603. end;
  3604. 'l' : begin
  3605. Mode := smodeOneLower;
  3606. p1 := p0;
  3607. end;
  3608. 'L' : begin
  3609. Mode := smodeAllLower;
  3610. p1 := p0;
  3611. end;
  3612. 'u' : begin
  3613. Mode := smodeOneUpper;
  3614. p1 := p0;
  3615. end;
  3616. 'U' : begin
  3617. Mode := smodeAllUpper;
  3618. p1 := p0;
  3619. end;
  3620. else
  3621. begin
  3622. inc(p0);
  3623. inc(p1);
  3624. end;
  3625. end;
  3626. end
  3627. end;
  3628. if p0 < p1 then begin
  3629. while p0 < p1 do begin
  3630. case Mode of
  3631. smodeOneLower, smodeAllLower:
  3632. begin
  3633. Ch := p0^;
  3634. Ch := AnsiLowerCase(Ch)[1];
  3635. ResultPtr^ := Ch;
  3636. if Mode = smodeOneLower then
  3637. Mode := smodeNormal;
  3638. end;
  3639. smodeOneUpper, smodeAllUpper:
  3640. begin
  3641. Ch := p0^;
  3642. Ch := AnsiUpperCase(Ch)[1];
  3643. ResultPtr^ := Ch;
  3644. if Mode = smodeOneUpper then
  3645. Mode := smodeNormal;
  3646. end;
  3647. else
  3648. ResultPtr^ := p0^;
  3649. end;
  3650. inc (ResultPtr);
  3651. inc (p0);
  3652. end;
  3653. Mode := smodeNormal;
  3654. end;
  3655. end;
  3656. end; { of function TRegExpr.Substitute
  3657. --------------------------------------------------------------}
  3658. procedure TRegExpr.Split (Const AInputStr : RegExprString; APieces : TStrings);
  3659. var PrevPos : PtrInt;
  3660. begin
  3661. PrevPos := 1;
  3662. if Exec (AInputStr) then
  3663. REPEAT
  3664. APieces.Add (System.Copy (AInputStr, PrevPos, MatchPos [0] - PrevPos));
  3665. PrevPos := MatchPos [0] + MatchLen [0];
  3666. UNTIL not ExecNext;
  3667. APieces.Add (System.Copy (AInputStr, PrevPos, MaxInt)); // Tail
  3668. end; { of procedure TRegExpr.Split
  3669. --------------------------------------------------------------}
  3670. function TRegExpr.Replace (Const AInputStr : RegExprString; const AReplaceStr : RegExprString;
  3671. AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString;
  3672. var
  3673. PrevPos : PtrInt;
  3674. begin
  3675. Result := '';
  3676. PrevPos := 1;
  3677. if Exec (AInputStr) then
  3678. REPEAT
  3679. Result := Result + System.Copy (AInputStr, PrevPos,
  3680. MatchPos [0] - PrevPos);
  3681. if AUseSubstitution //###0.946
  3682. then Result := Result + Substitute (AReplaceStr)
  3683. else Result := Result + AReplaceStr;
  3684. PrevPos := MatchPos [0] + MatchLen [0];
  3685. UNTIL not ExecNext;
  3686. Result := Result + System.Copy (AInputStr, PrevPos, MaxInt); // Tail
  3687. end; { of function TRegExpr.Replace
  3688. --------------------------------------------------------------}
  3689. function TRegExpr.ReplaceEx (Const AInputStr : RegExprString;
  3690. AReplaceFunc : TRegExprReplaceFunction)
  3691. : RegExprString;
  3692. var
  3693. PrevPos : PtrInt;
  3694. begin
  3695. Result := '';
  3696. PrevPos := 1;
  3697. if Exec (AInputStr) then
  3698. REPEAT
  3699. Result := Result + System.Copy (AInputStr, PrevPos,
  3700. MatchPos [0] - PrevPos)
  3701. + AReplaceFunc (Self);
  3702. PrevPos := MatchPos [0] + MatchLen [0];
  3703. UNTIL not ExecNext;
  3704. Result := Result + System.Copy (AInputStr, PrevPos, MaxInt); // Tail
  3705. end; { of function TRegExpr.ReplaceEx
  3706. --------------------------------------------------------------}
  3707. {$IFDEF OverMeth}
  3708. function TRegExpr.Replace (const AInputStr : RegExprString;
  3709. AReplaceFunc : TRegExprReplaceFunction)
  3710. : RegExprString;
  3711. begin
  3712. {$IFDEF FPC}Result:={$ENDIF}ReplaceEx (AInputStr, AReplaceFunc);
  3713. end; { of function TRegExpr.Replace
  3714. --------------------------------------------------------------}
  3715. {$ENDIF}
  3716. {=============================================================}
  3717. {====================== Debug section ========================}
  3718. {=============================================================}
  3719. {$IFDEF RegExpPCodeDump}
  3720. function TRegExpr.DumpOp (op : TREOp) : RegExprString;
  3721. // printable representation of opcode
  3722. begin
  3723. case op of
  3724. BOL: Result := 'BOL';
  3725. EOL: Result := 'EOL';
  3726. BOLML: Result := 'BOLML';
  3727. EOLML: Result := 'EOLML';
  3728. BOUND: Result := 'BOUND'; //###0.943
  3729. NOTBOUND: Result := 'NOTBOUND'; //###0.943
  3730. ANY: Result := 'ANY';
  3731. ANYML: Result := 'ANYML'; //###0.941
  3732. ANYLETTER: Result := 'ANYLETTER';
  3733. NOTLETTER: Result := 'NOTLETTER';
  3734. ANYDIGIT: Result := 'ANYDIGIT';
  3735. NOTDIGIT: Result := 'NOTDIGIT';
  3736. ANYSPACE: Result := 'ANYSPACE';
  3737. NOTSPACE: Result := 'NOTSPACE';
  3738. ANYOF: Result := 'ANYOF';
  3739. ANYBUT: Result := 'ANYBUT';
  3740. ANYOFCI: Result := 'ANYOF/CI';
  3741. ANYBUTCI: Result := 'ANYBUT/CI';
  3742. BRANCH: Result := 'BRANCH';
  3743. EXACTLY: Result := 'EXACTLY';
  3744. EXACTLYCI: Result := 'EXACTLY/CI';
  3745. NOTHING: Result := 'NOTHING';
  3746. COMMENT: Result := 'COMMENT';
  3747. BACK: Result := 'BACK';
  3748. EEND: Result := 'END';
  3749. BSUBEXP: Result := 'BSUBEXP';
  3750. BSUBEXPCI: Result := 'BSUBEXP/CI';
  3751. Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1): //###0.929
  3752. Result := Format ('OPEN[%d]', [ord (op) - ord (OPEN)]);
  3753. Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): //###0.929
  3754. Result := Format ('CLOSE[%d]', [ord (op) - ord (CLOSE)]);
  3755. STAR: Result := 'STAR';
  3756. PLUS: Result := 'PLUS';
  3757. BRACES: Result := 'BRACES';
  3758. {$IFDEF ComplexBraces}
  3759. LOOPENTRY: Result := 'LOOPENTRY'; //###0.925
  3760. LOOP: Result := 'LOOP'; //###0.925
  3761. LOOPNG: Result := 'LOOPNG'; //###0.940
  3762. {$ENDIF}
  3763. ANYOFTINYSET: Result:= 'ANYOFTINYSET';
  3764. ANYBUTTINYSET:Result:= 'ANYBUTTINYSET';
  3765. {$IFDEF UseSetOfChar} //###0.929
  3766. ANYOFFULLSET: Result:= 'ANYOFFULLSET';
  3767. {$ENDIF}
  3768. STARNG: Result := 'STARNG'; //###0.940
  3769. PLUSNG: Result := 'PLUSNG'; //###0.940
  3770. BRACESNG: Result := 'BRACESNG'; //###0.940
  3771. else Error (reeDumpCorruptedOpcode);
  3772. end; {of case op}
  3773. Result := ':' + Result;
  3774. end; { of function TRegExpr.DumpOp
  3775. --------------------------------------------------------------}
  3776. function TRegExpr.Dump : RegExprString;
  3777. // dump a regexp in vaguely comprehensible form
  3778. var
  3779. s : PRegExprChar;
  3780. op : TREOp; // Arbitrary non-END op.
  3781. next : PRegExprChar;
  3782. i : PtrInt;
  3783. Diff : PtrInt;
  3784. {$IFDEF UseSetOfChar} //###0.929
  3785. Ch : REChar;
  3786. {$ENDIF}
  3787. begin
  3788. if not IsProgrammOk //###0.929
  3789. then EXIT;
  3790. op := EXACTLY;
  3791. Result := '';
  3792. s := programm + REOpSz;
  3793. while op <> EEND do begin // While that wasn't END last time...
  3794. op := s^;
  3795. Result := Result + Format ('%2d%s', [s - programm, DumpOp (s^)]); // Where, what.
  3796. next := regnext (s);
  3797. if next = nil // Next ptr.
  3798. then Result := Result + ' (0)'
  3799. else begin
  3800. if next > s //###0.948 PWideChar subtraction workaround (see comments in Tail method for details)
  3801. then Diff := next - s
  3802. else Diff := - (s - next);
  3803. Result := Result + Format (' (%d) ', [(s - programm) + Diff]);
  3804. end;
  3805. inc (s, REOpSz + RENextOffSz);
  3806. if (op = ANYOF) or (op = ANYOFCI) or (op = ANYBUT) or (op = ANYBUTCI)
  3807. or (op = EXACTLY) or (op = EXACTLYCI) then begin
  3808. // Literal string, where present.
  3809. while s^ <> #0 do begin
  3810. Result := Result + s^;
  3811. inc (s);
  3812. end;
  3813. inc (s);
  3814. end;
  3815. if (op = ANYOFTINYSET) or (op = ANYBUTTINYSET) then begin
  3816. for i := 1 to TinySetLen do begin
  3817. Result := Result + s^;
  3818. inc (s);
  3819. end;
  3820. end;
  3821. if (op = BSUBEXP) or (op = BSUBEXPCI) then begin
  3822. Result := Result + ' \' + IntToStr (Ord (s^));
  3823. inc (s);
  3824. end;
  3825. {$IFDEF UseSetOfChar} //###0.929
  3826. if op = ANYOFFULLSET then begin
  3827. for Ch := #0 to #255 do
  3828. if Ch in PSetOfREChar (s)^ then
  3829. if Ch < ' '
  3830. then Result := Result + '#' + IntToStr (Ord (Ch)) //###0.936
  3831. else Result := Result + Ch;
  3832. inc (s, SizeOf (TSetOfREChar));
  3833. end;
  3834. {$ENDIF}
  3835. if (op = BRACES) or (op = BRACESNG) then begin //###0.941
  3836. // show min/max argument of BRACES operator
  3837. Result := Result + Format ('{%d,%d}', [PREBracesArg (AlignToInt(s))^, PREBracesArg (AlignToInt(s + REBracesArgSz))^]);
  3838. inc (s, REBracesArgSz * 2);
  3839. end;
  3840. {$IFDEF ComplexBraces}
  3841. if (op = LOOP) or (op = LOOPNG) then begin //###0.940
  3842. Result := Result + Format (' -> (%d) {%d,%d}', [
  3843. (s - programm - (REOpSz + RENextOffSz)) + PRENextOff (AlignToPtr(s + 2 * REBracesArgSz))^,
  3844. PREBracesArg (AlignToInt(s))^, PREBracesArg (AlignToInt(s + REBracesArgSz))^]);
  3845. inc (s, 2 * REBracesArgSz + RENextOffSz);
  3846. end;
  3847. {$ENDIF}
  3848. Result := Result + #$d#$a;
  3849. end; { of while}
  3850. // Header fields of interest.
  3851. if regstart <> #0
  3852. then Result := Result + 'start ' + regstart;
  3853. if reganch <> #0
  3854. then Result := Result + 'anchored ';
  3855. if regmust <> nil
  3856. then Result := Result + 'must have ' + regmust;
  3857. {$IFDEF UseFirstCharSet} //###0.929
  3858. Result := Result + #$d#$a'FirstCharSet:';
  3859. for Ch := #0 to #255 do
  3860. if Ch in FirstCharSet
  3861. then begin
  3862. if Ch < ' '
  3863. then Result := Result + '#' + IntToStr(Ord(Ch)) //###0.948
  3864. else Result := Result + Ch;
  3865. end;
  3866. {$ENDIF}
  3867. Result := Result + #$d#$a;
  3868. end; { of function TRegExpr.Dump
  3869. --------------------------------------------------------------}
  3870. {$ENDIF}
  3871. {$IFDEF reRealExceptionAddr}
  3872. {$OPTIMIZATION ON}
  3873. // ReturnAddr works correctly only if compiler optimization is ON
  3874. // I placed this method at very end of unit because there are no
  3875. // way to restore compiler optimization flag ...
  3876. {$ENDIF}
  3877. procedure TRegExpr.Error (AErrorID : integer);
  3878. {$IFDEF reRealExceptionAddr}
  3879. function ReturnAddr : pointer; //###0.938
  3880. asm
  3881. mov eax,[ebp+4]
  3882. end;
  3883. {$ENDIF}
  3884. var
  3885. e : ERegExpr;
  3886. begin
  3887. fLastError := AErrorID; // dummy stub - useless because will raise exception
  3888. if AErrorID < 1000 // compilation error ?
  3889. then e := ERegExpr.Create (ErrorMsg (AErrorID) // yes - show error pos
  3890. + ' (pos ' + IntToStr (CompilerErrorPos) + ')')
  3891. else e := ERegExpr.Create (ErrorMsg (AErrorID));
  3892. e.ErrorCode := AErrorID;
  3893. e.CompilerErrorPos := CompilerErrorPos;
  3894. raise e
  3895. {$IFDEF reRealExceptionAddr}
  3896. At ReturnAddr; //###0.938
  3897. {$ENDIF}
  3898. end; { of procedure TRegExpr.Error
  3899. --------------------------------------------------------------}
  3900. (*
  3901. PCode persistence:
  3902. FirstCharSet
  3903. programm, regsize
  3904. regstart // -> programm
  3905. reganch // -> programm
  3906. regmust, regmlen // -> programm
  3907. fExprIsCompiled
  3908. *)
  3909. // be carefull - placed here code will be always compiled with
  3910. // compiler optimization flag
  3911. {$IFDEF FPC}
  3912. initialization
  3913. RegExprInvertCaseFunction := TRegExpr.InvertCaseFunction;
  3914. {$ENDIF}
  3915. end.