regexpr.pas 145 KB

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