regexpr.pas 145 KB

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