regexpr.pas 145 KB

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