2
0

regexpr.pas 143 KB

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