regexpr.pas 157 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278
  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. https://sorokin.engineer/
  21. [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. https://sorokin.engineer/
  32. [email protected]
  33. Option 2>
  34. The same modified LGPL with static linking exception as the Free Pascal RTL
  35. }
  36. interface
  37. { off $DEFINE DebugSynRegExpr }
  38. {$MODE DELPHI} // Delphi-compatible mode in FreePascal
  39. // Disabling for now, seems to cause bug in Lazarus (bug ID 36603)
  40. { $INLINE ON}
  41. // ======== Define base compiler options
  42. {$BOOLEVAL OFF}
  43. {$EXTENDEDSYNTAX ON}
  44. {$LONGSTRINGS ON}
  45. {$OPTIMIZATION ON}
  46. // ======== Define options for TRegExpr engine
  47. {$DEFINE UseFirstCharSet} // Enable optimization, which finds possible first chars of input string
  48. {$DEFINE RegExpPCodeDump} // Enable method Dump() to show opcode as string
  49. {$DEFINE ComplexBraces} // Support braces in complex cases
  50. {$IFNDEF UniCode}
  51. {$UNDEF UnicodeWordDetection}
  52. {$ELSE}
  53. {$DEFINE UnicodeWordDetection}
  54. {$ENDIF}
  55. uses
  56. Math, // Min
  57. Classes, // TStrings in Split method
  58. SysUtils; // Exception
  59. type
  60. {$IFDEF UniCode}
  61. PRegExprChar = PWideChar;
  62. RegExprString = UnicodeString;
  63. REChar = WideChar;
  64. {$ELSE}
  65. PRegExprChar = PChar;
  66. RegExprString = AnsiString; // ###0.952 was string
  67. REChar = Char;
  68. {$ENDIF}
  69. TREOp = REChar; // internal p-code type //###0.933
  70. PREOp = ^TREOp;
  71. type
  72. TRegExprInvertCaseFunction = function(const Ch: REChar): REChar of object;
  73. TRegExprCharset = set of byte;
  74. const
  75. // Escape char ('\' in common r.e.) used for escaping metachars (\w, \d etc)
  76. EscChar = '\';
  77. RegExprModifierI: boolean = False; // default value for ModifierI
  78. RegExprModifierR: boolean = True; // default value for ModifierR
  79. RegExprModifierS: boolean = True; // default value for ModifierS
  80. RegExprModifierG: boolean = True; // default value for ModifierG
  81. RegExprModifierM: boolean = False; // default value for ModifierM
  82. RegExprModifierX: boolean = False; // default value for ModifierX
  83. // default value for SpaceChars
  84. RegExprSpaceChars: RegExprString = ' '#$9#$A#$D#$C;
  85. // default value for WordChars
  86. RegExprWordChars: RegExprString = '0123456789'
  87. + 'abcdefghijklmnopqrstuvwxyz'
  88. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';
  89. // default value for LineSeparators
  90. RegExprLineSeparators: RegExprString = #$d#$a#$b#$c
  91. {$IFDEF UniCode}
  92. + #$2028#$2029#$85
  93. {$ENDIF};
  94. // default value for LinePairedSeparator
  95. RegExprLinePairedSeparator: RegExprString = #$d#$a;
  96. { if You need Unix-styled line separators (only \n), then use:
  97. RegExprLineSeparators = #$a;
  98. RegExprLinePairedSeparator = '';
  99. }
  100. // Tab and Unicode category "Space Separator":
  101. // https://www.compart.com/en/unicode/category/Zs
  102. RegExprHorzSeparators: RegExprString = #9#$20#$A0
  103. {$IFDEF UniCode}
  104. + #$1680#$2000#$2001#$2002#$2003#$2004#$2005#$2006#$2007#$2008#$2009#$200A#$202F#$205F#$3000
  105. {$ENDIF};
  106. const
  107. NSUBEXP = 90; // max number of subexpression //###0.929
  108. // Cannot be more than NSUBEXPMAX
  109. // Be carefull - don't use values which overflow CLOSE opcode
  110. // (in this case you'll get compiler error).
  111. // Big NSUBEXP will cause more slow work and more stack required
  112. NSUBEXPMAX = 255; // Max possible value for NSUBEXP. //###0.945
  113. // Don't change it! It's defined by internal TRegExpr design.
  114. {$IFDEF ComplexBraces}
  115. const
  116. LoopStackMax = 10; // max depth of loops stack //###0.925
  117. type
  118. TRegExprLoopStack = array [1 .. LoopStackMax] of integer;
  119. {$ENDIF}
  120. type
  121. TRegExprModifiers = record
  122. I: boolean;
  123. // Case-insensitive.
  124. R: boolean;
  125. // Extended syntax for Russian ranges in [].
  126. // If True, then а-я additionally includes letter 'ё',
  127. // А-Я additionally includes 'Ё', and а-Я includes all Russian letters.
  128. // Turn it off if it interferes with your national alphabet.
  129. S: boolean;
  130. // Dot '.' matches any char, otherwise only [^\n].
  131. G: boolean;
  132. // Greedy. Switching it off switches all operators to non-greedy style,
  133. // so if G=False, then '*' works like '*?', '+' works like '+?' and so on.
  134. M: boolean;
  135. // Treat string as multiple lines. It changes `^' and `$' from
  136. // matching at only the very start/end of the string to the start/end
  137. // of any line anywhere within the string.
  138. X: boolean;
  139. // Allow comments in regex using # char.
  140. end;
  141. function IsModifiersEqual(const A, B: TRegExprModifiers): boolean;
  142. type
  143. TRegExpr = class;
  144. TRegExprReplaceFunction = function(ARegExpr: TRegExpr): RegExprString of object;
  145. TRegExprCharChecker = function(ch: REChar): boolean of object;
  146. TRegExprCharCheckerArray = array[0 .. 30] of TRegExprCharChecker;
  147. TRegExprCharCheckerInfo = record
  148. CharBegin, CharEnd: REChar;
  149. CheckerIndex: integer;
  150. end;
  151. TRegExprCharCheckerInfos = array of TRegExprCharCheckerInfo;
  152. { TRegExpr }
  153. TRegExpr = class
  154. private
  155. startp: array [0 .. NSUBEXP - 1] of PRegExprChar; // found expr start points
  156. endp: array [0 .. NSUBEXP - 1] of PRegExprChar; // found expr end points
  157. GrpIndexes: array [0 .. NSUBEXP - 1] of integer;
  158. GrpCount: integer;
  159. {$IFDEF ComplexBraces}
  160. LoopStack: TRegExprLoopStack; // state before entering loop
  161. LoopStackIdx: integer; // 0 - out of all loops
  162. {$ENDIF}
  163. // The "internal use only" fields to pass info from compile
  164. // to execute that permits the execute phase to run lots faster on
  165. // simple cases.
  166. reganchored: REChar; // is the match anchored (at beginning-of-line only)?
  167. regmust: PRegExprChar; // string (pointer into program) that match must include, or nil
  168. regmustlen: integer; // length of regmust string
  169. regmustString: RegExprString;
  170. // reganchored permits very fast decisions on suitable starting points
  171. // for a match, cutting down the work a lot. Regmust permits fast rejection
  172. // of lines that cannot possibly match. The regmust tests are costly enough
  173. // that regcomp() supplies a regmust only if the r.e. contains something
  174. // potentially expensive (at present, the only such thing detected is * or +
  175. // at the start of the r.e., which can involve a lot of backup). regmustlen is
  176. // supplied because the test in regexec() needs it and regcomp() is computing
  177. // it anyway.
  178. {$IFDEF UseFirstCharSet}
  179. FirstCharSet: TRegExprCharset;
  180. FirstCharArray: array[byte] of boolean;
  181. {$ENDIF}
  182. // work variables for Exec routines - save stack in recursion
  183. reginput: PRegExprChar; // String-input pointer.
  184. fInputStart: PRegExprChar; // Pointer to first char of input string.
  185. fInputEnd: PRegExprChar; // Pointer to char AFTER last char of input string
  186. fRegexStart: PRegExprChar;
  187. fRegexEnd: PRegExprChar;
  188. // work variables for compiler's routines
  189. regparse: PRegExprChar; // Input-scan pointer.
  190. regnpar: integer; // Count of () brackets.
  191. regdummy: REChar;
  192. regcode: PRegExprChar; // Code-emit pointer; @regdummy = don't.
  193. regsize: integer; // Total programm size in REChars.
  194. regExactlyLen: PLongInt;
  195. regexpBegin: PRegExprChar; // only for error handling. Contains pointer to beginning of r.e. while compiling
  196. regexpIsCompiled: boolean; // true if r.e. successfully compiled
  197. fSecondPass: boolean;
  198. // programm is essentially a linear encoding
  199. // of a nondeterministic finite-state machine (aka syntax charts or
  200. // "railroad normal form" in parsing technology). Each node is an opcode
  201. // plus a "next" pointer, possibly plus an operand. "Next" pointers of
  202. // all nodes except BRANCH implement concatenation; a "next" pointer with
  203. // a BRANCH on both ends of it connects two alternatives. (Here we
  204. // have one of the subtle syntax dependencies: an individual BRANCH (as
  205. // opposed to a collection of them) is never concatenated with anything
  206. // because of operator precedence.) The operand of some types of node is
  207. // a literal string; for others, it is a node leading into a sub-FSM. In
  208. // particular, the operand of a BRANCH node is the first node of the branch.
  209. // (NB this is *not* a tree structure: the tail of the branch connects
  210. // to the thing following the set of BRANCHes.) The opcodes are:
  211. programm: PRegExprChar; // Unwarranted chumminess with compiler.
  212. fExpression: RegExprString; // source of compiled r.e.
  213. fInputString: RegExprString; // input string
  214. fLastError: integer; // see Error, LastError
  215. fLastErrorOpcode: TREOp;
  216. fModifiers: TRegExprModifiers; // modifiers
  217. fCompModifiers: TRegExprModifiers; // compiler's copy of modifiers
  218. fProgModifiers: TRegExprModifiers; // modifiers values from last programm compilation
  219. fSpaceChars: RegExprString;
  220. fWordChars: RegExprString;
  221. fInvertCase: TRegExprInvertCaseFunction;
  222. fLineSeparators: RegExprString;
  223. fLinePairedSeparatorAssigned: boolean;
  224. fLinePairedSeparatorHead, fLinePairedSeparatorTail: REChar;
  225. FReplaceLineEnd: RegExprString; // string to use for "\n" in Substitute method
  226. FUseOsLineEndOnReplace: boolean; // use OS LineBreak chars (LF or CRLF) for FReplaceLineEnd
  227. fSlowChecksSizeMax: integer;
  228. // use ASlowChecks=True in Exec() only when Length(InputString)<SlowChecksSizeMax
  229. // ASlowChecks enables to use regmustString optimization
  230. {$IFNDEF UniCode}
  231. fLineSepArray: array[byte] of boolean;
  232. {$ENDIF}
  233. {$IFDEF UnicodeWordDetection}
  234. FUseUnicodeWordDetection: boolean;
  235. {$ENDIF}
  236. CharCheckers: TRegExprCharCheckerArray;
  237. CharCheckerInfos: TRegExprCharCheckerInfos;
  238. CheckerIndex_Word: byte;
  239. CheckerIndex_NotWord: byte;
  240. CheckerIndex_Digit: byte;
  241. CheckerIndex_NotDigit: byte;
  242. CheckerIndex_Space: byte;
  243. CheckerIndex_NotSpace: byte;
  244. CheckerIndex_HorzSep: byte;
  245. CheckerIndex_NotHorzSep: byte;
  246. CheckerIndex_VertSep: byte;
  247. CheckerIndex_NotVertSep: byte;
  248. CheckerIndex_LowerAZ: byte;
  249. CheckerIndex_UpperAZ: byte;
  250. procedure InitCharCheckers;
  251. function CharChecker_Word(ch: REChar): boolean;
  252. function CharChecker_NotWord(ch: REChar): boolean;
  253. function CharChecker_Space(ch: REChar): boolean;
  254. function CharChecker_NotSpace(ch: REChar): boolean;
  255. function CharChecker_Digit(ch: REChar): boolean;
  256. function CharChecker_NotDigit(ch: REChar): boolean;
  257. function CharChecker_HorzSep(ch: REChar): boolean;
  258. function CharChecker_NotHorzSep(ch: REChar): boolean;
  259. function CharChecker_VertSep(ch: REChar): boolean;
  260. function CharChecker_NotVertSep(ch: REChar): boolean;
  261. function CharChecker_LowerAZ(ch: REChar): boolean;
  262. function CharChecker_UpperAZ(ch: REChar): boolean;
  263. procedure ClearMatches; {$IFDEF InlineFuncs}inline;{$ENDIF}
  264. procedure ClearInternalIndexes; {$IFDEF InlineFuncs}inline;{$ENDIF}
  265. function FindInCharClass(ABuffer: PRegExprChar; AChar: REChar; AIgnoreCase: boolean): boolean;
  266. procedure GetCharSetFromCharClass(ABuffer: PRegExprChar; AIgnoreCase: boolean; var ARes: TRegExprCharset);
  267. procedure GetCharSetFromSpaceChars(var ARes: TRegExprCharset);
  268. procedure GetCharSetFromWordChars(var ARes: TRegExprCharSet);
  269. function IsWordChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  270. function IsSpaceChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  271. function IsCustomLineSeparator(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  272. procedure InitLineSepArray;
  273. // Mark programm as having to be [re]compiled
  274. procedure InvalidateProgramm;
  275. // Check if we can use precompiled r.e. or
  276. // [re]compile it if something changed
  277. function IsProgrammOk: boolean; // ###0.941
  278. procedure SetExpression(const AStr: RegExprString);
  279. function GetModifierStr: RegExprString;
  280. procedure SetModifierStr(const AStr: RegExprString);
  281. function GetModifierG: boolean;
  282. function GetModifierI: boolean;
  283. function GetModifierM: boolean;
  284. function GetModifierR: boolean;
  285. function GetModifierS: boolean;
  286. function GetModifierX: boolean;
  287. procedure SetModifierG(AValue: boolean);
  288. procedure SetModifierI(AValue: boolean);
  289. procedure SetModifierM(AValue: boolean);
  290. procedure SetModifierR(AValue: boolean);
  291. procedure SetModifierS(AValue: boolean);
  292. procedure SetModifierX(AValue: boolean);
  293. // Default handler raises exception ERegExpr with
  294. // Message = ErrorMsg (AErrorID), ErrorCode = AErrorID
  295. // and CompilerErrorPos = value of property CompilerErrorPos.
  296. procedure Error(AErrorID: integer); virtual; // error handler.
  297. { ==================== Compiler section =================== }
  298. // compile a regular expression into internal code
  299. function CompileRegExpr(ARegExp: PRegExprChar): boolean;
  300. procedure SetUseOsLineEndOnReplace(AValue: boolean);
  301. // set the next-pointer at the end of a node chain
  302. procedure Tail(p: PRegExprChar; val: PRegExprChar);
  303. // regoptail - regtail on operand of first argument; nop if operandless
  304. procedure OpTail(p: PRegExprChar; val: PRegExprChar);
  305. // regnode - emit a node, return location
  306. function EmitNode(op: TREOp): PRegExprChar;
  307. // emit (if appropriate) a byte of code
  308. procedure EmitC(ch: REChar);
  309. // emit LongInt value
  310. procedure EmitInt(AValue: LongInt);
  311. // insert an operator in front of already-emitted operand
  312. // Means relocating the operand.
  313. procedure InsertOperator(op: TREOp; opnd: PRegExprChar; sz: integer);
  314. // ###0.90
  315. // regular expression, i.e. main body or parenthesized thing
  316. function ParseReg(paren: integer; var flagp: integer): PRegExprChar;
  317. // one alternative of an | operator
  318. function ParseBranch(var flagp: integer): PRegExprChar;
  319. // something followed by possible [*+?]
  320. function ParsePiece(var flagp: integer): PRegExprChar;
  321. function HexDig(Ch: REChar): integer;
  322. function UnQuoteChar(var APtr: PRegExprChar): REChar;
  323. // the lowest level
  324. function ParseAtom(var flagp: integer): PRegExprChar;
  325. // current pos in r.e. - for error hanling
  326. function GetCompilerErrorPos: PtrInt;
  327. {$IFDEF UseFirstCharSet} // ###0.929
  328. procedure FillFirstCharSet(prog: PRegExprChar);
  329. {$ENDIF}
  330. { ===================== Matching section =================== }
  331. // repeatedly match something simple, report how many
  332. function regrepeat(p: PRegExprChar; AMax: integer): integer;
  333. // dig the "next" pointer out of a node
  334. function regnext(p: PRegExprChar): PRegExprChar;
  335. // recursively matching routine
  336. function MatchPrim(prog: PRegExprChar): boolean;
  337. // match at specific position only, called from ExecPrim
  338. function MatchAtOnePos(APos: PRegExprChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  339. // Exec for stored InputString
  340. function ExecPrim(AOffset: integer; ATryOnce, ASlowChecks: boolean): boolean;
  341. {$IFDEF RegExpPCodeDump}
  342. function DumpOp(op: TREOp): RegExprString;
  343. {$ENDIF}
  344. function GetSubExprCount: integer;
  345. function GetMatchPos(Idx: integer): PtrInt;
  346. function GetMatchLen(Idx: integer): PtrInt;
  347. function GetMatch(Idx: integer): RegExprString;
  348. procedure SetInputString(const AInputString: RegExprString);
  349. procedure SetLineSeparators(const AStr: RegExprString);
  350. procedure SetLinePairedSeparator(const AStr: RegExprString);
  351. function GetLinePairedSeparator: RegExprString;
  352. public
  353. constructor Create; overload;
  354. constructor Create(const AExpression: RegExprString); overload;
  355. destructor Destroy; override;
  356. class function VersionMajor: integer;
  357. class function VersionMinor: integer;
  358. // match a programm against a string AInputString
  359. // !!! Exec store AInputString into InputString property
  360. // For Delphi 5 and higher available overloaded versions - first without
  361. // parameter (uses already assigned to InputString property value)
  362. // and second that has int parameter and is same as ExecPos
  363. function Exec(const AInputString: RegExprString): boolean; overload;
  364. function Exec: boolean; overload;
  365. function Exec(AOffset: integer): boolean; overload;
  366. // find next match:
  367. // ExecNext;
  368. // works the same as
  369. // if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)
  370. // else ExecPos (MatchPos [0] + MatchLen [0]);
  371. // but it's more simpler !
  372. // Raises exception if used without preceeding SUCCESSFUL call to
  373. // Exec* (Exec, ExecPos, ExecNext). So You always must use something like
  374. // if Exec (InputString) then repeat { proceed results} until not ExecNext;
  375. function ExecNext: boolean;
  376. // find match for InputString starting from AOffset position
  377. // (AOffset=1 - first char of InputString)
  378. function ExecPos(AOffset: integer = 1): boolean; overload;
  379. function ExecPos(AOffset: integer; ATryOnce: boolean): boolean; overload;
  380. // Returns ATemplate with '$&' or '$0' replaced by whole r.e.
  381. // occurence and '$1'...'$nn' replaced by subexpression with given index.
  382. // Symbol '$' is used instead of '\' (for future extensions
  383. // and for more Perl-compatibility) and accepts more than one digit.
  384. // If you want to place into template raw '$' or '\', use prefix '\'.
  385. // Example: '1\$ is $2\\rub\\' -> '1$ is <Match[2]>\rub\'
  386. // If you want to place any number after '$' you must enclose it
  387. // with curly braces: '${12}'.
  388. // Example: 'a$12bc' -> 'a<Match[12]>bc'
  389. // 'a${1}2bc' -> 'a<Match[1]>2bc'.
  390. function Substitute(const ATemplate: RegExprString): RegExprString;
  391. // Splits AInputStr to list by positions of all r.e. occurencies.
  392. // Internally calls Exec, ExecNext.
  393. procedure Split(const AInputStr: RegExprString; APieces: TStrings);
  394. function Replace(const AInputStr: RegExprString;
  395. const AReplaceStr: RegExprString;
  396. AUseSubstitution: boolean = False) // ###0.946
  397. : RegExprString; overload;
  398. function Replace(const AInputStr: RegExprString;
  399. AReplaceFunc: TRegExprReplaceFunction): RegExprString; overload;
  400. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr.
  401. // If AUseSubstitution is true, then AReplaceStr will be used
  402. // as template for Substitution methods.
  403. // For example:
  404. // Expression := '({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*';
  405. // Replace ('BLOCK( test1)', 'def "$1" value "$2"', True);
  406. // will return: def 'BLOCK' value 'test1'
  407. // Replace ('BLOCK( test1)', 'def "$1" value "$2"')
  408. // will return: def "$1" value "$2"
  409. // Internally calls Exec, ExecNext.
  410. // Overloaded version and ReplaceEx operate with callback function,
  411. // so you can implement really complex functionality.
  412. function ReplaceEx(const AInputStr: RegExprString;
  413. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  414. // Returns ID of last error, 0 if no errors (unusable if
  415. // Error method raises exception) and clear internal status
  416. // into 0 (no errors).
  417. function LastError: integer;
  418. // Returns Error message for error with ID = AErrorID.
  419. function ErrorMsg(AErrorID: integer): RegExprString; virtual;
  420. // Converts Ch into upper case if it in lower case or in lower
  421. // if it in upper (uses current system local setings)
  422. class function InvertCaseFunction(const Ch: REChar): REChar;
  423. // [Re]compile r.e. Useful for example for GUI r.e. editors (to check
  424. // all properties validity).
  425. procedure Compile; // ###0.941
  426. {$IFDEF RegExpPCodeDump}
  427. // dump a compiled regexp in vaguely comprehensible form
  428. function Dump: RegExprString;
  429. {$ENDIF}
  430. // Regular expression.
  431. // For optimization, TRegExpr will automatically compiles it into 'P-code'
  432. // (You can see it with help of Dump method) and stores in internal
  433. // structures. Real [re]compilation occures only when it really needed -
  434. // while calling Exec, ExecNext, Substitute, Dump, etc
  435. // and only if Expression or other P-code affected properties was changed
  436. // after last [re]compilation.
  437. // If any errors while [re]compilation occures, Error method is called
  438. // (by default Error raises exception - see below)
  439. property Expression: RegExprString read fExpression write SetExpression;
  440. // Set/get default values of r.e.syntax modifiers. Modifiers in
  441. // r.e. (?ismx-ismx) will replace this default values.
  442. // If you try to set unsupported modifier, Error will be called
  443. // (by defaul Error raises exception ERegExpr).
  444. property ModifierStr: RegExprString read GetModifierStr write SetModifierStr;
  445. property ModifierI: boolean read GetModifierI write SetModifierI;
  446. property ModifierR: boolean read GetModifierR write SetModifierR;
  447. property ModifierS: boolean read GetModifierS write SetModifierS;
  448. property ModifierG: boolean read GetModifierG write SetModifierG;
  449. property ModifierM: boolean read GetModifierM write SetModifierM;
  450. property ModifierX: boolean read GetModifierX write SetModifierX;
  451. // returns current input string (from last Exec call or last assign
  452. // to this property).
  453. // Any assignment to this property clear Match* properties !
  454. property InputString: RegExprString read fInputString write SetInputString;
  455. // Number of subexpressions has been found in last Exec* call.
  456. // If there are no subexpr. but whole expr was found (Exec* returned True),
  457. // then SubExprMatchCount=0, if no subexpressions nor whole
  458. // r.e. found (Exec* returned false) then SubExprMatchCount=-1.
  459. // Note, that some subexpr. may be not found and for such
  460. // subexpr. MathPos=MatchLen=-1 and Match=''.
  461. // For example: Expression := '(1)?2(3)?';
  462. // Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'
  463. // Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'
  464. // Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'
  465. // Exec ('2'): SubExprMatchCount=0, Match[0]='2'
  466. // Exec ('7') - return False: SubExprMatchCount=-1
  467. property SubExprMatchCount: integer read GetSubExprCount;
  468. // pos of entrance subexpr. #Idx into tested in last Exec*
  469. // string. First subexpr. has Idx=1, last - MatchCount,
  470. // whole r.e. has Idx=0.
  471. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  472. // not found in input string.
  473. property MatchPos[Idx: integer]: PtrInt read GetMatchPos;
  474. // len of entrance subexpr. #Idx r.e. into tested in last Exec*
  475. // string. First subexpr. has Idx=1, last - MatchCount,
  476. // whole r.e. has Idx=0.
  477. // Returns -1 if in r.e. no such subexpr. or this subexpr.
  478. // not found in input string.
  479. // Remember - MatchLen may be 0 (if r.e. match empty string) !
  480. property MatchLen[Idx: integer]: PtrInt read GetMatchLen;
  481. // == copy (InputString, MatchPos [Idx], MatchLen [Idx])
  482. // Returns '' if in r.e. no such subexpr. or this subexpr.
  483. // not found in input string.
  484. property Match[Idx: integer]: RegExprString read GetMatch;
  485. // Returns position in r.e. where compiler stopped.
  486. // Useful for error diagnostics
  487. property CompilerErrorPos: PtrInt read GetCompilerErrorPos;
  488. // Contains chars, treated as /s (initially filled with RegExprSpaceChars
  489. // global constant)
  490. property SpaceChars: RegExprString read fSpaceChars write fSpaceChars;
  491. // ###0.927
  492. // Contains chars, treated as /w (initially filled with RegExprWordChars
  493. // global constant)
  494. property WordChars: RegExprString read fWordChars write fWordChars;
  495. // ###0.929
  496. {$IFDEF UnicodeWordDetection}
  497. // If set to true, in addition to using WordChars, a heuristic to detect unicode word letters is used for \w
  498. property UseUnicodeWordDetection: boolean read FUseUnicodeWordDetection write FUseUnicodeWordDetection;
  499. {$ENDIF}
  500. // line separators (like \n in Unix)
  501. property LineSeparators: RegExprString read fLineSeparators write SetLineSeparators; // ###0.941
  502. // paired line separator (like \r\n in DOS and Windows).
  503. // must contain exactly two chars or no chars at all
  504. property LinePairedSeparator: RegExprString read GetLinePairedSeparator write SetLinePairedSeparator; // ###0.941
  505. // Set this property if you want to override case-insensitive functionality.
  506. // Create set it to RegExprInvertCaseFunction (InvertCaseFunction by default)
  507. property InvertCase: TRegExprInvertCaseFunction read fInvertCase write fInvertCase; // ##0.935
  508. // Use OS line end on replace or not. Default is True for backwards compatibility.
  509. // Set to false to use #10.
  510. property UseOsLineEndOnReplace: boolean read FUseOsLineEndOnReplace write SetUseOsLineEndOnReplace;
  511. property SlowChecksSizeMax: integer read fSlowChecksSizeMax write fSlowChecksSizeMax;
  512. end;
  513. type
  514. ERegExpr = class(Exception)
  515. public
  516. ErrorCode: integer;
  517. CompilerErrorPos: PtrInt;
  518. end;
  519. const
  520. RegExprInvertCaseFunction: TRegExprInvertCaseFunction = nil;
  521. // true if string AInputString match regular expression ARegExpr
  522. // ! will raise exeption if syntax errors in ARegExpr
  523. function ExecRegExpr(const ARegExpr, AInputStr: RegExprString): boolean;
  524. // Split AInputStr into APieces by r.e. ARegExpr occurencies
  525. procedure SplitRegExpr(const ARegExpr, AInputStr: RegExprString;
  526. APieces: TStrings);
  527. // Returns AInputStr with r.e. occurencies replaced by AReplaceStr
  528. // If AUseSubstitution is true, then AReplaceStr will be used
  529. // as template for Substitution methods.
  530. // For example:
  531. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  532. // 'BLOCK( test1)', 'def "$1" value "$2"', True)
  533. // will return: def 'BLOCK' value 'test1'
  534. // ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
  535. // 'BLOCK( test1)', 'def "$1" value "$2"')
  536. // will return: def "$1" value "$2"
  537. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  538. AUseSubstitution: boolean = False): RegExprString; overload; // ###0.947
  539. // Alternate form allowing to set more parameters.
  540. type
  541. TRegexReplaceOption = (
  542. rroModifierI,
  543. rroModifierR,
  544. rroModifierS,
  545. rroModifierG,
  546. rroModifierM,
  547. rroModifierX,
  548. rroUseSubstitution,
  549. rroUseOsLineEnd
  550. );
  551. TRegexReplaceOptions = set of TRegexReplaceOption;
  552. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  553. Options: TRegexReplaceOptions): RegExprString; overload;
  554. // Replace all metachars with its safe representation,
  555. // for example 'abc$cd.(' converts into 'abc\$cd\.\('
  556. // This function useful for r.e. autogeneration from
  557. // user input
  558. function QuoteRegExprMetaChars(const AStr: RegExprString): RegExprString;
  559. // Makes list of subexpressions found in ARegExpr r.e.
  560. // In ASubExps every item represent subexpression,
  561. // from first to last, in format:
  562. // String - subexpression text (without '()')
  563. // low word of Object - starting position in ARegExpr, including '('
  564. // if exists! (first position is 1)
  565. // high word of Object - length, including starting '(' and ending ')'
  566. // if exist!
  567. // AExtendedSyntax - must be True if modifier /m will be On while
  568. // using the r.e.
  569. // Useful for GUI editors of r.e. etc (You can find example of using
  570. // in TestRExp.dpr project)
  571. // Returns
  572. // 0 Success. No unbalanced brackets was found;
  573. // -1 There are not enough closing brackets ')';
  574. // -(n+1) At position n was found opening '[' without //###0.942
  575. // corresponding closing ']';
  576. // n At position n was found closing bracket ')' without
  577. // corresponding opening '('.
  578. // If Result <> 0, then ASubExpr can contain empty items or illegal ones
  579. function RegExprSubExpressions(const ARegExpr: string; ASubExprs: TStrings;
  580. AExtendedSyntax: boolean= False): integer;
  581. implementation
  582. {$IFDEF UnicodeWordDetection}
  583. uses
  584. UnicodeData;
  585. {$ENDIF}
  586. const
  587. // TRegExpr.VersionMajor/Minor return values of these constants:
  588. REVersionMajor = 0;
  589. REVersionMinor = 987;
  590. OpKind_End = REChar(1);
  591. OpKind_MetaClass = REChar(2);
  592. OpKind_Range = REChar(3);
  593. OpKind_Char = REChar(4);
  594. RegExprAllSet = [0 .. 255];
  595. RegExprDigitSet = [Ord('0') .. Ord('9')];
  596. RegExprLowerAzSet = [Ord('a') .. Ord('z')];
  597. RegExprUpperAzSet = [Ord('A') .. Ord('Z')];
  598. RegExprAllAzSet = RegExprLowerAzSet + RegExprUpperAzSet;
  599. RegExprLineSeparatorsSet = [$d, $a, $b, $c] {$IFDEF UniCode} + [$85] {$ENDIF};
  600. RegExprHorzSeparatorsSet = [9, $20, $A0];
  601. MaxBracesArg = $7FFFFFFF - 1; // max value for {n,m} arguments //###0.933
  602. type
  603. TRENextOff = PtrInt;
  604. // internal Next "pointer" (offset to current p-code) //###0.933
  605. PRENextOff = ^TRENextOff;
  606. // used for extracting Next "pointers" from compiled r.e. //###0.933
  607. TREBracesArg = integer; // type of {m,n} arguments
  608. PREBracesArg = ^TREBracesArg;
  609. const
  610. REOpSz = SizeOf(TREOp) div SizeOf(REChar);
  611. // size of OP_ command in REChars
  612. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  613. // add space for aligning pointer
  614. // -1 is the correct max size but also needed for InsertOperator that needs a multiple of pointer size
  615. RENextOffSz = (2 * SizeOf(TRENextOff) div SizeOf(REChar)) - 1;
  616. REBracesArgSz = (2 * SizeOf(TREBracesArg) div SizeOf(REChar));
  617. // add space for aligning pointer
  618. {$ELSE}
  619. RENextOffSz = (SizeOf(TRENextOff) div SizeOf(REChar));
  620. // size of Next pointer in REChars
  621. REBracesArgSz = SizeOf(TREBracesArg) div SizeOf(REChar);
  622. // size of BRACES arguments in REChars
  623. {$ENDIF}
  624. RENumberSz = SizeOf(LongInt) div SizeOf(REChar);
  625. function _FindCharInBuffer(SBegin, SEnd: PRegExprChar; Ch: REChar): PRegExprChar; {$IFDEF InlineFuncs}inline;{$ENDIF}
  626. begin
  627. while SBegin < SEnd do
  628. begin
  629. if SBegin^ = Ch then
  630. begin
  631. Result := SBegin;
  632. Exit;
  633. end;
  634. Inc(SBegin);
  635. end;
  636. Result := nil;
  637. end;
  638. function IsIgnoredChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  639. begin
  640. case AChar of
  641. ' ', #9, #$d, #$a:
  642. Result := True
  643. else
  644. Result := False;
  645. end;
  646. end;
  647. function _IsMetaChar(AChar: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  648. begin
  649. case AChar of
  650. 'd', 'D',
  651. 's', 'S',
  652. 'w', 'W',
  653. 'v', 'V',
  654. 'h', 'H':
  655. Result := True
  656. else
  657. Result := False;
  658. end;
  659. end;
  660. function AlignToPtr(const p: Pointer): Pointer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  661. begin
  662. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  663. Result := Align(p, SizeOf(Pointer));
  664. {$ELSE}
  665. Result := p;
  666. {$ENDIF}
  667. end;
  668. function AlignToInt(const p: Pointer): Pointer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  669. begin
  670. {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT}
  671. Result := Align(p, SizeOf(integer));
  672. {$ELSE}
  673. Result := p;
  674. {$ENDIF}
  675. end;
  676. function _UpperCase(Ch: REChar): REChar;
  677. begin
  678. Result := Ch;
  679. if (Ch >= 'a') and (Ch <= 'z') then
  680. begin
  681. Dec(Result, 32);
  682. Exit;
  683. end;
  684. if Ord(Ch) < 128 then
  685. Exit;
  686. {$IFDEF FPC}
  687. {$IFDEF UniCode}
  688. Result := UnicodeUpperCase(Ch)[1];
  689. {$ELSE}
  690. Result := AnsiUpperCase(Ch)[1];
  691. {$ENDIF}
  692. {$ELSE}
  693. {$IFDEF UniCode}
  694. {$IFDEF D2009}
  695. Result := TCharacter.ToUpper(Ch);
  696. {$ENDIF}
  697. {$ELSE}
  698. Result := AnsiUpperCase(Ch)[1];
  699. {$ENDIF}
  700. {$ENDIF}
  701. end;
  702. function _LowerCase(Ch: REChar): REChar;
  703. begin
  704. Result := Ch;
  705. if (Ch >= 'A') and (Ch <= 'Z') then
  706. begin
  707. Inc(Result, 32);
  708. Exit;
  709. end;
  710. if Ord(Ch) < 128 then
  711. Exit;
  712. {$IFDEF FPC}
  713. {$IFDEF UniCode}
  714. Result := UnicodeLowerCase(Ch)[1];
  715. {$ELSE}
  716. Result := AnsiLowerCase(Ch)[1];
  717. {$ENDIF}
  718. {$ELSE}
  719. {$IFDEF UniCode}
  720. {$IFDEF D2009}
  721. Result := TCharacter.ToLower(Ch);
  722. {$ENDIF}
  723. {$ELSE}
  724. Result := AnsiLowerCase(Ch)[1];
  725. {$ENDIF}
  726. {$ENDIF}
  727. end;
  728. { ============================================================= }
  729. { ===================== Global functions ====================== }
  730. { ============================================================= }
  731. function IsModifiersEqual(const A, B: TRegExprModifiers): boolean;
  732. begin
  733. Result :=
  734. (A.I = B.I) and
  735. (A.G = B.G) and
  736. (A.M = B.M) and
  737. (A.S = B.S) and
  738. (A.R = B.R) and
  739. (A.X = B.X);
  740. end;
  741. function ParseModifiers(const APtr: PRegExprChar;
  742. ALen: integer;
  743. var AValue: TRegExprModifiers): boolean;
  744. // Parse string and set AValue if it's in format 'ismxrg-ismxrg'
  745. var
  746. IsOn: boolean;
  747. i: integer;
  748. begin
  749. Result := True;
  750. IsOn := True;
  751. for i := 0 to ALen-1 do
  752. case APtr[i] of
  753. '-':
  754. IsOn := False;
  755. 'I', 'i':
  756. AValue.I := IsOn;
  757. 'R', 'r':
  758. AValue.R := IsOn;
  759. 'S', 's':
  760. AValue.S := IsOn;
  761. 'G', 'g':
  762. AValue.G := IsOn;
  763. 'M', 'm':
  764. AValue.M := IsOn;
  765. 'X', 'x':
  766. AValue.X := IsOn;
  767. else
  768. begin
  769. Result := False;
  770. Exit;
  771. end;
  772. end;
  773. end;
  774. function ExecRegExpr(const ARegExpr, AInputStr: RegExprString): boolean;
  775. var
  776. r: TRegExpr;
  777. begin
  778. r := TRegExpr.Create;
  779. try
  780. r.Expression := ARegExpr;
  781. Result := r.Exec(AInputStr);
  782. finally
  783. r.Free;
  784. end;
  785. end; { of function ExecRegExpr
  786. -------------------------------------------------------------- }
  787. procedure SplitRegExpr(const ARegExpr, AInputStr: RegExprString;
  788. APieces: TStrings);
  789. var
  790. r: TRegExpr;
  791. begin
  792. APieces.Clear;
  793. r := TRegExpr.Create;
  794. try
  795. r.Expression := ARegExpr;
  796. r.Split(AInputStr, APieces);
  797. finally
  798. r.Free;
  799. end;
  800. end; { of procedure SplitRegExpr
  801. -------------------------------------------------------------- }
  802. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  803. AUseSubstitution: boolean= False): RegExprString;
  804. begin
  805. with TRegExpr.Create do
  806. try
  807. Expression := ARegExpr;
  808. Result := Replace(AInputStr, AReplaceStr, AUseSubstitution);
  809. finally
  810. Free;
  811. end;
  812. end; { of function ReplaceRegExpr
  813. -------------------------------------------------------------- }
  814. function ReplaceRegExpr(const ARegExpr, AInputStr, AReplaceStr: RegExprString;
  815. Options: TRegexReplaceOptions): RegExprString; overload;
  816. begin
  817. with TRegExpr.Create do
  818. try
  819. ModifierI := (rroModifierI in Options);
  820. ModifierR := (rroModifierR in Options);
  821. ModifierS := (rroModifierS in Options);
  822. ModifierG := (rroModifierG in Options);
  823. ModifierM := (rroModifierM in Options);
  824. ModifierX := (rroModifierX in Options);
  825. // Set this after the above, if the regex contains modifiers, they will be applied.
  826. Expression := ARegExpr;
  827. UseOsLineEndOnReplace := (rroUseOsLineEnd in Options);
  828. Result := Replace(AInputStr, AReplaceStr, rroUseSubstitution in Options);
  829. finally
  830. Free;
  831. end;
  832. end;
  833. (*
  834. const
  835. MetaChars_Init = '^$.[()|?+*' + EscChar + '{';
  836. MetaChars = MetaChars_Init; // not needed to be a variable, const is faster
  837. MetaAll = MetaChars_Init + ']}'; // Very similar to MetaChars, but slighly changed.
  838. *)
  839. function _IsMetaSymbol1(ch: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  840. begin
  841. case ch of
  842. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{':
  843. Result := True
  844. else
  845. Result := False
  846. end;
  847. end;
  848. function _IsMetaSymbol2(ch: REChar): boolean; {$IFDEF InlineFuncs}inline;{$ENDIF}
  849. begin
  850. case ch of
  851. '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{',
  852. ']', '}':
  853. Result := True
  854. else
  855. Result := False
  856. end;
  857. end;
  858. function QuoteRegExprMetaChars(const AStr: RegExprString): RegExprString;
  859. var
  860. i, i0, Len: integer;
  861. ch: REChar;
  862. begin
  863. Result := '';
  864. Len := Length(AStr);
  865. i := 1;
  866. i0 := i;
  867. while i <= Len do
  868. begin
  869. ch := AStr[i];
  870. if _IsMetaSymbol2(ch) then
  871. begin
  872. Result := Result + System.Copy(AStr, i0, i - i0) + EscChar + ch;
  873. i0 := i + 1;
  874. end;
  875. Inc(i);
  876. end;
  877. Result := Result + System.Copy(AStr, i0, MaxInt); // Tail
  878. end; { of function QuoteRegExprMetaChars
  879. -------------------------------------------------------------- }
  880. function RegExprSubExpressions(const ARegExpr: string; ASubExprs: TStrings;
  881. AExtendedSyntax: boolean = False): integer;
  882. type
  883. TStackItemRec = record // ###0.945
  884. SubExprIdx: integer;
  885. StartPos: PtrInt;
  886. end;
  887. TStackArray = packed array [0 .. NSUBEXPMAX - 1] of TStackItemRec;
  888. var
  889. Len, SubExprLen: integer;
  890. i, i0: integer;
  891. Modif: TRegExprModifiers;
  892. Stack: ^TStackArray; // ###0.945
  893. StackIdx, StackSz: integer;
  894. begin
  895. Result := 0; // no unbalanced brackets found at this very moment
  896. Modif:=Default(TRegExprModifiers);
  897. ASubExprs.Clear; // I don't think that adding to non empty list
  898. // can be useful, so I simplified algorithm to work only with empty list
  899. Len := Length(ARegExpr); // some optimization tricks
  900. // first we have to calculate number of subexpression to reserve
  901. // space in Stack array (may be we'll reserve more than needed, but
  902. // it's faster then memory reallocation during parsing)
  903. StackSz := 1; // add 1 for entire r.e.
  904. for i := 1 to Len do
  905. if ARegExpr[i] = '(' then
  906. Inc(StackSz);
  907. // SetLength (Stack, StackSz); //###0.945
  908. GetMem(Stack, SizeOf(TStackItemRec) * StackSz);
  909. try
  910. StackIdx := 0;
  911. i := 1;
  912. while (i <= Len) do
  913. begin
  914. case ARegExpr[i] of
  915. '(':
  916. begin
  917. if (i < Len) and (ARegExpr[i + 1] = '?') then
  918. begin
  919. // this is not subexpression, but comment or other
  920. // Perl extension. We must check is it (?ismxrg-ismxrg)
  921. // and change AExtendedSyntax if /x is changed.
  922. Inc(i, 2); // skip '(?'
  923. i0 := i;
  924. while (i <= Len) and (ARegExpr[i] <> ')') do
  925. Inc(i);
  926. if i > Len then
  927. Result := -1 // unbalansed '('
  928. else
  929. if ParseModifiers(@ARegExpr[i0], i - i0, Modif) then
  930. // Alexey-T: original code had copy from i, not from i0
  931. AExtendedSyntax := Modif.X;
  932. end
  933. else
  934. begin // subexpression starts
  935. ASubExprs.Add(''); // just reserve space
  936. with Stack[StackIdx] do
  937. begin
  938. SubExprIdx := ASubExprs.Count - 1;
  939. StartPos := i;
  940. end;
  941. Inc(StackIdx);
  942. end;
  943. end;
  944. ')':
  945. begin
  946. if StackIdx = 0 then
  947. Result := i // unbalanced ')'
  948. else
  949. begin
  950. Dec(StackIdx);
  951. with Stack[StackIdx] do
  952. begin
  953. SubExprLen := i - StartPos + 1;
  954. ASubExprs.Objects[SubExprIdx] :=
  955. TObject(StartPos or (SubExprLen ShL 16));
  956. ASubExprs[SubExprIdx] := System.Copy(ARegExpr, StartPos + 1,
  957. SubExprLen - 2); // add without brackets
  958. end;
  959. end;
  960. end;
  961. EscChar:
  962. Inc(i); // skip quoted symbol
  963. '[':
  964. begin
  965. // we have to skip character ranges at once, because they can
  966. // contain '#', and '#' in it must NOT be recognized as eXtended
  967. // comment beginning!
  968. i0 := i;
  969. Inc(i);
  970. if ARegExpr[i] = ']' // first ']' inside [] treated as simple char, no need to check '['
  971. then
  972. Inc(i);
  973. while (i <= Len) and (ARegExpr[i] <> ']') do
  974. if ARegExpr[i] = EscChar // ###0.942
  975. then
  976. Inc(i, 2) // skip 'escaped' char to prevent stopping at '\]'
  977. else
  978. Inc(i);
  979. if (i > Len) or (ARegExpr[i] <> ']') // ###0.942
  980. then
  981. Result := -(i0 + 1); // unbalansed '[' //###0.942
  982. end;
  983. '#':
  984. if AExtendedSyntax then
  985. begin
  986. // skip eXtended comments
  987. while (i <= Len) and (ARegExpr[i] <> #$d) and (ARegExpr[i] <> #$a)
  988. // do not use [#$d, #$a] due to UniCode compatibility
  989. do
  990. Inc(i);
  991. while (i + 1 <= Len) and
  992. ((ARegExpr[i + 1] = #$d) or (ARegExpr[i + 1] = #$a)) do
  993. Inc(i); // attempt to work with different kinds of line separators
  994. // now we are at the line separator that must be skipped.
  995. end;
  996. // here is no 'else' clause - we simply skip ordinary chars
  997. end; // of case
  998. Inc(i); // skip scanned char
  999. // ! can move after Len due to skipping quoted symbol
  1000. end;
  1001. // check brackets balance
  1002. if StackIdx <> 0 then
  1003. Result := -1; // unbalansed '('
  1004. // check if entire r.e. added
  1005. if (ASubExprs.Count = 0) or ((PtrInt(ASubExprs.Objects[0]) and $FFFF) <> 1)
  1006. or (((PtrInt(ASubExprs.Objects[0]) ShR 16) and $FFFF) <> Len)
  1007. // whole r.e. wasn't added because it isn't bracketed
  1008. // well, we add it now:
  1009. then
  1010. ASubExprs.InsertObject(0, ARegExpr, TObject((Len ShL 16) or 1));
  1011. finally
  1012. FreeMem(Stack);
  1013. end;
  1014. end; { of function RegExprSubExpressions
  1015. -------------------------------------------------------------- }
  1016. const
  1017. OP_MAGIC = TREOp(216); // programm signature
  1018. // name opcode opnd? meaning
  1019. OP_EEND = TREOp(0); // - End of program
  1020. OP_BOL = TREOp(1); // - Match "" at beginning of line
  1021. OP_EOL = TREOp(2); // - Match "" at end of line
  1022. OP_ANY = TREOp(3); // - Match any one character
  1023. OP_ANYOF = TREOp(4); // Str Match any character in string Str
  1024. OP_ANYBUT = TREOp(5); // Str Match any char. not in string Str
  1025. OP_BRANCH = TREOp(6); // Node Match this alternative, or the next
  1026. OP_BACK = TREOp(7); // - Jump backward (Next < 0)
  1027. OP_EXACTLY = TREOp(8); // Str Match string Str
  1028. OP_NOTHING = TREOp(9); // - Match empty string
  1029. OP_STAR = TREOp(10); // Node Match this (simple) thing 0 or more times
  1030. OP_PLUS = TREOp(11); // Node Match this (simple) thing 1 or more times
  1031. OP_ANYDIGIT = TREOp(12); // - Match any digit (equiv [0-9])
  1032. OP_NOTDIGIT = TREOp(13); // - Match not digit (equiv [0-9])
  1033. OP_ANYLETTER = TREOp(14); // - Match any letter from property WordChars
  1034. OP_NOTLETTER = TREOp(15); // - Match not letter from property WordChars
  1035. OP_ANYSPACE = TREOp(16); // - Match any space char (see property SpaceChars)
  1036. OP_NOTSPACE = TREOp(17); // - Match not space char (see property SpaceChars)
  1037. OP_BRACES = TREOp(18);
  1038. // Node,Min,Max Match this (simple) thing from Min to Max times.
  1039. // Min and Max are TREBracesArg
  1040. OP_COMMENT = TREOp(19); // - Comment ;)
  1041. OP_EXACTLYCI = TREOp(20); // Str Match string Str case insensitive
  1042. OP_ANYOFCI = TREOp(21);
  1043. // Str Match any character in string Str, case insensitive
  1044. OP_ANYBUTCI = TREOp(22);
  1045. // Str Match any char. not in string Str, case insensitive
  1046. OP_LOOPENTRY = TREOp(23); // Node Start of loop (Node - LOOP for this loop)
  1047. OP_LOOP = TREOp(24); // Node,Min,Max,LoopEntryJmp - back jump for LOOPENTRY.
  1048. // Min and Max are TREBracesArg
  1049. // Node - next node in sequence,
  1050. // LoopEntryJmp - associated LOOPENTRY node addr
  1051. OP_BSUBEXP = TREOp(28);
  1052. // Idx Match previously matched subexpression #Idx (stored as REChar) //###0.936
  1053. OP_BSUBEXPCI = TREOp(29); // Idx -"- in case-insensitive mode
  1054. // Non-Greedy Style Ops //###0.940
  1055. OP_STARNG = TREOp(30); // Same as OP_START but in non-greedy mode
  1056. OP_PLUSNG = TREOp(31); // Same as OP_PLUS but in non-greedy mode
  1057. OP_BRACESNG = TREOp(32); // Same as OP_BRACES but in non-greedy mode
  1058. OP_LOOPNG = TREOp(33); // Same as OP_LOOP but in non-greedy mode
  1059. // Multiline mode \m
  1060. OP_BOLML = TREOp(34); // - Match "" at beginning of line
  1061. OP_EOLML = TREOp(35); // - Match "" at end of line
  1062. OP_ANYML = TREOp(36); // - Match any one character
  1063. // Word boundary
  1064. OP_BOUND = TREOp(37); // Match "" between words //###0.943
  1065. OP_NOTBOUND = TREOp(38); // Match "" not between words //###0.943
  1066. OP_ANYHORZSEP = TREOp(39); // Any horizontal whitespace \h
  1067. OP_NOTHORZSEP = TREOp(40); // Not horizontal whitespace \H
  1068. OP_ANYVERTSEP = TREOp(41); // Any vertical whitespace \v
  1069. OP_NOTVERTSEP = TREOp(42); // Not vertical whitespace \V
  1070. // !!! Change OP_OPEN value if you add new opcodes !!!
  1071. OP_OPEN = TREOp(43); // - Mark this point in input as start of \n
  1072. // OP_OPEN + 1 is \1, etc.
  1073. OP_CLOSE = TREOp(Ord(OP_OPEN) + NSUBEXP);
  1074. // - Analogous to OP_OPEN.
  1075. // !!! Don't add new OpCodes after CLOSE !!!
  1076. // We work with p-code through pointers, compatible with PRegExprChar.
  1077. // Note: all code components (TRENextOff, TREOp, TREBracesArg, etc)
  1078. // must have lengths that can be divided by SizeOf (REChar) !
  1079. // A node is TREOp of opcode followed Next "pointer" of TRENextOff type.
  1080. // The Next is a offset from the opcode of the node containing it.
  1081. // An operand, if any, simply follows the node. (Note that much of
  1082. // the code generation knows about this implicit relationship!)
  1083. // Using TRENextOff=PtrInt speed up p-code processing.
  1084. // Opcodes description:
  1085. //
  1086. // BRANCH The set of branches constituting a single choice are hooked
  1087. // together with their "next" pointers, since precedence prevents
  1088. // anything being concatenated to any individual branch. The
  1089. // "next" pointer of the last BRANCH in a choice points to the
  1090. // thing following the whole choice. This is also where the
  1091. // final "next" pointer of each individual branch points; each
  1092. // branch starts with the operand node of a BRANCH node.
  1093. // BACK Normal "next" pointers all implicitly point forward; BACK
  1094. // exists to make loop structures possible.
  1095. // STAR,PLUS,BRACES '?', and complex '*' and '+', are implemented as
  1096. // circular BRANCH structures using BACK. Complex '{min,max}'
  1097. // - as pair LOOPENTRY-LOOP (see below). Simple cases (one
  1098. // character per match) are implemented with STAR, PLUS and
  1099. // BRACES for speed and to minimize recursive plunges.
  1100. // LOOPENTRY,LOOP {min,max} are implemented as special pair
  1101. // LOOPENTRY-LOOP. Each LOOPENTRY initialize loopstack for
  1102. // current level.
  1103. // OPEN,CLOSE are numbered at compile time.
  1104. { ============================================================= }
  1105. { ================== Error handling section =================== }
  1106. { ============================================================= }
  1107. const
  1108. reeOk = 0;
  1109. reeCompNullArgument = 100;
  1110. reeCompParseRegTooManyBrackets = 102;
  1111. reeCompParseRegUnmatchedBrackets = 103;
  1112. reeCompParseRegUnmatchedBrackets2 = 104;
  1113. reeCompParseRegJunkOnEnd = 105;
  1114. reePlusStarOperandCouldBeEmpty = 106;
  1115. reeNestedSQP = 107;
  1116. reeBadHexDigit = 108;
  1117. reeInvalidRange = 109;
  1118. reeParseAtomTrailingBackSlash = 110;
  1119. reeNoHexCodeAfterBSlashX = 111;
  1120. reeHexCodeAfterBSlashXTooBig = 112;
  1121. reeUnmatchedSqBrackets = 113;
  1122. reeInternalUrp = 114;
  1123. reeQPSBFollowsNothing = 115;
  1124. reeTrailingBackSlash = 116;
  1125. reeNoLetterAfterBSlashC = 117;
  1126. reeMetaCharAfterMinusInRange = 118;
  1127. reeRarseAtomInternalDisaster = 119;
  1128. reeIncorrectBraces = 121;
  1129. reeBRACESArgTooBig = 122;
  1130. reeUnknownOpcodeInFillFirst = 123;
  1131. reeBracesMinParamGreaterMax = 124;
  1132. reeUnclosedComment = 125;
  1133. reeComplexBracesNotImplemented = 126;
  1134. reeUnrecognizedModifier = 127;
  1135. reeBadLinePairedSeparator = 128;
  1136. // Runtime errors must be >= 1000
  1137. reeRegRepeatCalledInappropriately = 1000;
  1138. reeMatchPrimMemoryCorruption = 1001;
  1139. reeMatchPrimCorruptedPointers = 1002;
  1140. reeNoExpression = 1003;
  1141. reeCorruptedProgram = 1004;
  1142. reeNoInputStringSpecified = 1005;
  1143. reeOffsetMustBePositive = 1006;
  1144. reeExecNextWithoutExec = 1007;
  1145. reeBadOpcodeInCharClass = 1008;
  1146. reeDumpCorruptedOpcode = 1011;
  1147. reeModifierUnsupported = 1013;
  1148. reeLoopStackExceeded = 1014;
  1149. reeLoopWithoutEntry = 1015;
  1150. function TRegExpr.ErrorMsg(AErrorID: integer): RegExprString;
  1151. begin
  1152. case AErrorID of
  1153. reeOk:
  1154. Result := 'No errors';
  1155. reeCompNullArgument:
  1156. Result := 'TRegExpr compile: null argument';
  1157. reeCompParseRegTooManyBrackets:
  1158. Result := 'TRegExpr compile: ParseReg: too many ()';
  1159. reeCompParseRegUnmatchedBrackets:
  1160. Result := 'TRegExpr compile: ParseReg: unmatched ()';
  1161. reeCompParseRegUnmatchedBrackets2:
  1162. Result := 'TRegExpr compile: ParseReg: unmatched ()';
  1163. reeCompParseRegJunkOnEnd:
  1164. Result := 'TRegExpr compile: ParseReg: junk at end';
  1165. reePlusStarOperandCouldBeEmpty:
  1166. Result := 'TRegExpr compile: *+ operand could be empty';
  1167. reeNestedSQP:
  1168. Result := 'TRegExpr compile: nested *?+';
  1169. reeBadHexDigit:
  1170. Result := 'TRegExpr compile: bad hex digit';
  1171. reeInvalidRange:
  1172. Result := 'TRegExpr compile: invalid [] range';
  1173. reeParseAtomTrailingBackSlash:
  1174. Result := 'TRegExpr compile: parse atom trailing \';
  1175. reeNoHexCodeAfterBSlashX:
  1176. Result := 'TRegExpr compile: no hex code after \x';
  1177. reeNoLetterAfterBSlashC:
  1178. Result := 'TRegExpr compile: no letter "A".."Z" after \c';
  1179. reeMetaCharAfterMinusInRange:
  1180. Result := 'TRegExpr compile: metachar after "-" in [] range';
  1181. reeHexCodeAfterBSlashXTooBig:
  1182. Result := 'TRegExpr compile: hex code after \x is too big';
  1183. reeUnmatchedSqBrackets:
  1184. Result := 'TRegExpr compile: unmatched []';
  1185. reeInternalUrp:
  1186. Result := 'TRegExpr compile: internal fail on char "|", ")"';
  1187. reeQPSBFollowsNothing:
  1188. Result := 'TRegExpr compile: ?+*{ follows nothing';
  1189. reeTrailingBackSlash:
  1190. Result := 'TRegExpr compile: trailing \';
  1191. reeRarseAtomInternalDisaster:
  1192. Result := 'TRegExpr compile: RarseAtom internal disaster';
  1193. reeIncorrectBraces:
  1194. Result := 'TRegExpr compile: incorrect {} braces';
  1195. reeBRACESArgTooBig:
  1196. Result := 'TRegExpr compile: braces {} argument too big';
  1197. reeUnknownOpcodeInFillFirst:
  1198. Result := 'TRegExpr compile: unknown opcode in FillFirstCharSet ('+DumpOp(fLastErrorOpcode)+')';
  1199. reeBracesMinParamGreaterMax:
  1200. Result := 'TRegExpr compile: braces {} min param greater then max';
  1201. reeUnclosedComment:
  1202. Result := 'TRegExpr compile: unclosed (?#comment)';
  1203. reeComplexBracesNotImplemented:
  1204. Result := 'TRegExpr compile: if you use braces {} and non-greedy ops *?, +?, ?? for complex cases, enable {$DEFINE ComplexBraces}';
  1205. reeUnrecognizedModifier:
  1206. Result := 'TRegExpr compile: unrecognized modifier';
  1207. reeBadLinePairedSeparator:
  1208. Result := 'TRegExpr compile: LinePairedSeparator must countain two different chars or be empty';
  1209. reeRegRepeatCalledInappropriately:
  1210. Result := 'TRegExpr exec: RegRepeat called inappropriately';
  1211. reeMatchPrimMemoryCorruption:
  1212. Result := 'TRegExpr exec: MatchPrim memory corruption';
  1213. reeMatchPrimCorruptedPointers:
  1214. Result := 'TRegExpr exec: MatchPrim corrupted pointers';
  1215. reeNoExpression:
  1216. Result := 'TRegExpr exec: empty expression';
  1217. reeCorruptedProgram:
  1218. Result := 'TRegExpr exec: corrupted opcode (no magic byte)';
  1219. reeNoInputStringSpecified:
  1220. Result := 'TRegExpr exec: empty input string';
  1221. reeOffsetMustBePositive:
  1222. Result := 'TRegExpr exec: offset must be >0';
  1223. reeExecNextWithoutExec:
  1224. Result := 'TRegExpr exec: ExecNext without Exec(Pos)';
  1225. reeBadOpcodeInCharClass:
  1226. Result := 'TRegExpr exec: invalid opcode in char class';
  1227. reeDumpCorruptedOpcode:
  1228. Result := 'TRegExpr dump: corrupted opcode';
  1229. reeLoopStackExceeded:
  1230. Result := 'TRegExpr exec: loop stack exceeded';
  1231. reeLoopWithoutEntry:
  1232. Result := 'TRegExpr exec: loop without loop entry';
  1233. else
  1234. Result := 'Unknown error';
  1235. end;
  1236. end; { of procedure TRegExpr.Error
  1237. -------------------------------------------------------------- }
  1238. function TRegExpr.LastError: integer;
  1239. begin
  1240. Result := fLastError;
  1241. fLastError := reeOk;
  1242. end; { of function TRegExpr.LastError
  1243. -------------------------------------------------------------- }
  1244. { ============================================================= }
  1245. { ===================== Common section ======================== }
  1246. { ============================================================= }
  1247. class function TRegExpr.VersionMajor: integer;
  1248. begin
  1249. Result := REVersionMajor;
  1250. end;
  1251. class function TRegExpr.VersionMinor: integer;
  1252. begin
  1253. Result := REVersionMinor;
  1254. end;
  1255. constructor TRegExpr.Create;
  1256. begin
  1257. inherited;
  1258. programm := nil;
  1259. fExpression := '';
  1260. fInputString := '';
  1261. regexpBegin := nil;
  1262. regexpIsCompiled := False;
  1263. FillChar(fModifiers, SIzeOf(fModifiers), 0);
  1264. ModifierI := RegExprModifierI;
  1265. ModifierR := RegExprModifierR;
  1266. ModifierS := RegExprModifierS;
  1267. ModifierG := RegExprModifierG;
  1268. ModifierM := RegExprModifierM;
  1269. ModifierX := RegExprModifierX;
  1270. SpaceChars := RegExprSpaceChars; // ###0.927
  1271. WordChars := RegExprWordChars; // ###0.929
  1272. fInvertCase := RegExprInvertCaseFunction; // ###0.927
  1273. fLineSeparators := RegExprLineSeparators; // ###0.941
  1274. LinePairedSeparator := RegExprLinePairedSeparator; // ###0.941
  1275. FUseOsLineEndOnReplace := True;
  1276. FReplaceLineEnd := sLineBreak;
  1277. {$IFDEF UnicodeWordDetection}
  1278. FUseUnicodeWordDetection := True;
  1279. {$ENDIF}
  1280. fSlowChecksSizeMax := 2000;
  1281. InitLineSepArray;
  1282. InitCharCheckers;
  1283. end; { of constructor TRegExpr.Create
  1284. -------------------------------------------------------------- }
  1285. constructor TRegExpr.Create(const AExpression: RegExprString);
  1286. begin
  1287. Create;
  1288. Expression := AExpression;
  1289. end;
  1290. destructor TRegExpr.Destroy;
  1291. begin
  1292. if programm <> nil then
  1293. begin
  1294. FreeMem(programm);
  1295. programm := nil;
  1296. end;
  1297. end; { of destructor TRegExpr.Destroy
  1298. -------------------------------------------------------------- }
  1299. class function TRegExpr.InvertCaseFunction(const Ch: REChar): REChar;
  1300. begin
  1301. Result := Ch;
  1302. if (Ch >= 'a') and (Ch <= 'z') then
  1303. begin
  1304. Dec(Result, 32);
  1305. Exit;
  1306. end;
  1307. if (Ch >= 'A') and (Ch <= 'Z') then
  1308. begin
  1309. Inc(Result, 32);
  1310. Exit;
  1311. end;
  1312. if Ord(Ch) < 128 then
  1313. Exit;
  1314. Result := _UpperCase(Ch);
  1315. if Result = Ch then
  1316. Result := _LowerCase(Ch);
  1317. Result := _UpperCase(Ch);
  1318. if Result = Ch then
  1319. Result := _LowerCase(Ch);
  1320. end; { of function TRegExpr.InvertCaseFunction
  1321. -------------------------------------------------------------- }
  1322. procedure TRegExpr.SetExpression(const AStr: RegExprString);
  1323. begin
  1324. if (AStr <> fExpression) or not regexpIsCompiled then
  1325. begin
  1326. regexpIsCompiled := False;
  1327. fExpression := AStr;
  1328. UniqueString(fExpression);
  1329. fRegexStart := PRegExprChar(fExpression);
  1330. fRegexEnd := fRegexStart + Length(fExpression);
  1331. InvalidateProgramm; // ###0.941
  1332. end;
  1333. end; { of procedure TRegExpr.SetExpression
  1334. -------------------------------------------------------------- }
  1335. function TRegExpr.GetSubExprCount: integer;
  1336. begin
  1337. // if nothing found, we must return -1 per TRegExpr docs
  1338. if startp[0] = nil then
  1339. Result := -1
  1340. else
  1341. Result := GrpCount;
  1342. end;
  1343. function TRegExpr.GetMatchPos(Idx: integer): PtrInt;
  1344. begin
  1345. Idx := GrpIndexes[Idx];
  1346. if (Idx >= 0) and (startp[Idx] <> nil) then
  1347. Result := startp[Idx] - fInputStart + 1
  1348. else
  1349. Result := -1;
  1350. end; { of function TRegExpr.GetMatchPos
  1351. -------------------------------------------------------------- }
  1352. function TRegExpr.GetMatchLen(Idx: integer): PtrInt;
  1353. begin
  1354. Idx := GrpIndexes[Idx];
  1355. if (Idx >= 0) and (startp[Idx] <> nil) then
  1356. Result := endp[Idx] - startp[Idx]
  1357. else
  1358. Result := -1;
  1359. end; { of function TRegExpr.GetMatchLen
  1360. -------------------------------------------------------------- }
  1361. function TRegExpr.GetMatch(Idx: integer): RegExprString;
  1362. begin
  1363. Result := '';
  1364. Idx := GrpIndexes[Idx];
  1365. if (Idx >= 0) and (endp[Idx] > startp[Idx]) then
  1366. SetString(Result, startp[Idx], endp[Idx] - startp[Idx]);
  1367. {
  1368. // then Result := copy (fInputString, MatchPos [Idx], MatchLen [Idx]) //###0.929
  1369. then
  1370. begin
  1371. SetLength(Result, endp[Idx] - startp[Idx]);
  1372. System.Move(startp[Idx]^, Result[1], Length(Result) * SizeOf(REChar));
  1373. end;
  1374. }
  1375. end; { of function TRegExpr.GetMatch
  1376. -------------------------------------------------------------- }
  1377. function TRegExpr.GetModifierStr: RegExprString;
  1378. begin
  1379. Result := '-';
  1380. if ModifierI then
  1381. Result := 'i' + Result
  1382. else
  1383. Result := Result + 'i';
  1384. if ModifierR then
  1385. Result := 'r' + Result
  1386. else
  1387. Result := Result + 'r';
  1388. if ModifierS then
  1389. Result := 's' + Result
  1390. else
  1391. Result := Result + 's';
  1392. if ModifierG then
  1393. Result := 'g' + Result
  1394. else
  1395. Result := Result + 'g';
  1396. if ModifierM then
  1397. Result := 'm' + Result
  1398. else
  1399. Result := Result + 'm';
  1400. if ModifierX then
  1401. Result := 'x' + Result
  1402. else
  1403. Result := Result + 'x';
  1404. if Result[Length(Result)] = '-' // remove '-' if all modifiers are 'On'
  1405. then
  1406. System.Delete(Result, Length(Result), 1);
  1407. end; { of function TRegExpr.GetModifierStr
  1408. -------------------------------------------------------------- }
  1409. procedure TRegExpr.SetModifierG(AValue: boolean);
  1410. begin
  1411. fModifiers.G := AValue;
  1412. end;
  1413. procedure TRegExpr.SetModifierI(AValue: boolean);
  1414. begin
  1415. fModifiers.I := AValue;
  1416. end;
  1417. procedure TRegExpr.SetModifierM(AValue: boolean);
  1418. begin
  1419. fModifiers.M := AValue;
  1420. end;
  1421. procedure TRegExpr.SetModifierR(AValue: boolean);
  1422. begin
  1423. fModifiers.R := AValue;
  1424. end;
  1425. procedure TRegExpr.SetModifierS(AValue: boolean);
  1426. begin
  1427. fModifiers.S := AValue;
  1428. end;
  1429. procedure TRegExpr.SetModifierX(AValue: boolean);
  1430. begin
  1431. fModifiers.X := AValue;
  1432. end;
  1433. procedure TRegExpr.SetModifierStr(const AStr: RegExprString);
  1434. begin
  1435. if not ParseModifiers(PRegExprChar(AStr), Length(AStr), fModifiers) then
  1436. Error(reeModifierUnsupported);
  1437. end; { of procedure TRegExpr.SetModifierStr
  1438. -------------------------------------------------------------- }
  1439. { ============================================================= }
  1440. { ==================== Compiler section ======================= }
  1441. { ============================================================= }
  1442. {$IFDEF UnicodeWordDetection}
  1443. {$IFDEF FPC}
  1444. function IsUnicodeWordChar(AChar: WideChar): boolean; inline;
  1445. var
  1446. NType: byte;
  1447. begin
  1448. if Ord(AChar) >= LOW_SURROGATE_BEGIN then
  1449. Exit(False);
  1450. NType := GetProps(Ord(AChar))^.Category;
  1451. Result := (NType <= UGC_OtherNumber);
  1452. end;
  1453. {$ELSE}
  1454. function IsUnicodeWordChar(AChar: WideChar): boolean; inline;
  1455. begin
  1456. Result := System.Character.IsLetterOrDigit(AChar);
  1457. end;
  1458. {$ENDIF}
  1459. {$ENDIF}
  1460. function TRegExpr.IsWordChar(AChar: REChar): boolean;
  1461. begin
  1462. Result := Pos(AChar, fWordChars) > 0;
  1463. {$IFDEF UnicodeWordDetection}
  1464. if not Result and (Ord(AChar) >= 128) and UseUnicodeWordDetection then
  1465. Result := IsUnicodeWordChar(AChar);
  1466. {$ENDIF}
  1467. end;
  1468. function TRegExpr.IsSpaceChar(AChar: REChar): boolean;
  1469. begin
  1470. Result := Pos(AChar, fSpaceChars) > 0;
  1471. end;
  1472. function TRegExpr.IsCustomLineSeparator(AChar: REChar): boolean;
  1473. begin
  1474. {$IFDEF UniCode}
  1475. Result := Pos(AChar, fLineSeparators) > 0;
  1476. {$ELSE}
  1477. Result := fLineSepArray[byte(AChar)];
  1478. {$ENDIF}
  1479. end;
  1480. function IsDigitChar(AChar: REChar): boolean; inline;
  1481. begin
  1482. case AChar of
  1483. '0' .. '9':
  1484. Result := True;
  1485. else
  1486. Result := False;
  1487. end;
  1488. end;
  1489. function IsHorzSeparator(AChar: REChar): boolean; inline;
  1490. begin
  1491. // Tab and Unicode categoty "Space Separator": https://www.compart.com/en/unicode/category/Zs
  1492. case AChar of
  1493. #9, #$20, #$A0:
  1494. Result := True;
  1495. {$IFDEF UniCode}
  1496. #$1680, #$2000 .. #$200A, #$202F, #$205F, #$3000:
  1497. Result := True;
  1498. {$ENDIF}
  1499. else
  1500. Result := False;
  1501. end;
  1502. end;
  1503. function IsLineSeparator(AChar: REChar): boolean; inline;
  1504. begin
  1505. case AChar of
  1506. #$d, #$a, #$b, #$c:
  1507. Result := True;
  1508. {$IFDEF UniCode}
  1509. #$2028, #$2029, #$85:
  1510. Result := True;
  1511. {$ENDIF}
  1512. else
  1513. Result := False;
  1514. end;
  1515. end;
  1516. procedure TRegExpr.InvalidateProgramm;
  1517. begin
  1518. if programm <> nil then
  1519. begin
  1520. FreeMem(programm);
  1521. programm := nil;
  1522. end;
  1523. end; { of procedure TRegExpr.InvalidateProgramm
  1524. -------------------------------------------------------------- }
  1525. procedure TRegExpr.Compile;
  1526. begin
  1527. if fExpression = '' then
  1528. begin
  1529. Error(reeNoExpression);
  1530. Exit;
  1531. end;
  1532. CompileRegExpr(PRegExprChar(fExpression));
  1533. end; { of procedure TRegExpr.Compile
  1534. -------------------------------------------------------------- }
  1535. procedure TRegExpr.InitLineSepArray;
  1536. {$IFNDEF UniCode}
  1537. var
  1538. i: integer;
  1539. {$ENDIF}
  1540. begin
  1541. {$IFNDEF UniCode}
  1542. FillChar(fLineSepArray, SizeOf(fLineSepArray), 0);
  1543. for i := 1 to Length(fLineSeparators) do
  1544. fLineSepArray[byte(fLineSeparators[i])] := True;
  1545. {$ENDIF}
  1546. end;
  1547. function TRegExpr.IsProgrammOk: boolean;
  1548. begin
  1549. Result := False;
  1550. // check modifiers
  1551. if not IsModifiersEqual(fModifiers, fProgModifiers) // ###0.941
  1552. then
  1553. InvalidateProgramm;
  1554. // [Re]compile if needed
  1555. if programm = nil then
  1556. begin
  1557. Compile; // ###0.941
  1558. // Check [re]compiled programm
  1559. if programm = nil then
  1560. Exit; // error was set/raised by Compile (was reeExecAfterCompErr)
  1561. end;
  1562. if programm[0] <> OP_MAGIC // Program corrupted.
  1563. then
  1564. Error(reeCorruptedProgram)
  1565. else
  1566. Result := True;
  1567. end; { of function TRegExpr.IsProgrammOk
  1568. -------------------------------------------------------------- }
  1569. procedure TRegExpr.Tail(p: PRegExprChar; val: PRegExprChar);
  1570. // set the next-pointer at the end of a node chain
  1571. var
  1572. scan: PRegExprChar;
  1573. temp: PRegExprChar;
  1574. begin
  1575. if p = @regdummy then
  1576. Exit;
  1577. // Find last node.
  1578. scan := p;
  1579. repeat
  1580. temp := regnext(scan);
  1581. if temp = nil then
  1582. Break;
  1583. scan := temp;
  1584. until False;
  1585. // Set Next 'pointer'
  1586. if val < scan then
  1587. PRENextOff(AlignToPtr(scan + REOpSz))^ := -(scan - val) // ###0.948
  1588. // work around PWideChar subtraction bug (Delphi uses
  1589. // shr after subtraction to calculate widechar distance %-( )
  1590. // so, if difference is negative we have .. the "feature" :(
  1591. // I could wrap it in $IFDEF UniCode, but I didn't because
  1592. // "P – Q computes the difference between the address given
  1593. // by P (the higher address) and the address given by Q (the
  1594. // lower address)" - Delphi help quotation.
  1595. else
  1596. PRENextOff(AlignToPtr(scan + REOpSz))^ := val - scan; // ###0.933
  1597. end; { of procedure TRegExpr.Tail
  1598. -------------------------------------------------------------- }
  1599. procedure TRegExpr.OpTail(p: PRegExprChar; val: PRegExprChar);
  1600. // regtail on operand of first argument; nop if operandless
  1601. begin
  1602. // "Operandless" and "op != OP_BRANCH" are synonymous in practice.
  1603. if (p = nil) or (p = @regdummy) or (PREOp(p)^ <> OP_BRANCH) then
  1604. Exit;
  1605. Tail(p + REOpSz + RENextOffSz, val); // ###0.933
  1606. end; { of procedure TRegExpr.OpTail
  1607. -------------------------------------------------------------- }
  1608. function TRegExpr.EmitNode(op: TREOp): PRegExprChar; // ###0.933
  1609. // emit a node, return location
  1610. begin
  1611. Result := regcode;
  1612. if Result <> @regdummy then
  1613. begin
  1614. PREOp(regcode)^ := op;
  1615. Inc(regcode, REOpSz);
  1616. PRENextOff(AlignToPtr(regcode))^ := 0; // Next "pointer" := nil
  1617. Inc(regcode, RENextOffSz);
  1618. if (op = OP_EXACTLY) or (op = OP_EXACTLYCI) then
  1619. regExactlyLen := PLongInt(regcode)
  1620. else
  1621. regExactlyLen := nil;
  1622. {$IFDEF DebugSynRegExpr}
  1623. if regcode - programm > regsize then
  1624. raise Exception.Create('TRegExpr.EmitNode buffer overrun');
  1625. {$ENDIF}
  1626. end
  1627. else
  1628. Inc(regsize, REOpSz + RENextOffSz);
  1629. // compute code size without code generation
  1630. end; { of function TRegExpr.EmitNode
  1631. -------------------------------------------------------------- }
  1632. procedure TRegExpr.EmitC(ch: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  1633. begin
  1634. if regcode <> @regdummy then
  1635. begin
  1636. regcode^ := ch;
  1637. Inc(regcode);
  1638. {$IFDEF DebugSynRegExpr}
  1639. if regcode - programm > regsize then
  1640. raise Exception.Create('TRegExpr.EmitC buffer overrun');
  1641. {$ENDIF}
  1642. end
  1643. else
  1644. Inc(regsize, REOpSz); // Type of p-code pointer always is ^REChar
  1645. end; { of procedure TRegExpr.EmitC
  1646. -------------------------------------------------------------- }
  1647. procedure TRegExpr.EmitInt(AValue: LongInt); {$IFDEF InlineFuncs}inline;{$ENDIF}
  1648. begin
  1649. if regcode <> @regdummy then
  1650. begin
  1651. PLongInt(regcode)^ := AValue;
  1652. Inc(regcode, RENumberSz);
  1653. {$IFDEF DebugSynRegExpr}
  1654. if regcode - programm > regsize then
  1655. raise Exception.Create('TRegExpr.EmitInt buffer overrun');
  1656. {$ENDIF}
  1657. end
  1658. else
  1659. Inc(regsize, RENumberSz);
  1660. end;
  1661. procedure TRegExpr.InsertOperator(op: TREOp; opnd: PRegExprChar; sz: integer);
  1662. // insert an operator in front of already-emitted operand
  1663. // Means relocating the operand.
  1664. var
  1665. src, dst, place: PRegExprChar;
  1666. i: integer;
  1667. begin
  1668. if regcode = @regdummy then
  1669. begin
  1670. Inc(regsize, sz);
  1671. Exit;
  1672. end;
  1673. // move code behind insert position
  1674. src := regcode;
  1675. Inc(regcode, sz);
  1676. {$IFDEF DebugSynRegExpr}
  1677. if regcode - programm > regsize then
  1678. raise Exception.Create('TRegExpr.InsertOperator buffer overrun');
  1679. // if (opnd<regcode) or (opnd-regcode>regsize) then
  1680. // raise Exception.Create('TRegExpr.InsertOperator invalid opnd');
  1681. {$ENDIF}
  1682. dst := regcode;
  1683. while src > opnd do
  1684. begin
  1685. Dec(dst);
  1686. Dec(src);
  1687. dst^ := src^;
  1688. end;
  1689. place := opnd; // Op node, where operand used to be.
  1690. PREOp(place)^ := op;
  1691. Inc(place, REOpSz);
  1692. for i := 1 + REOpSz to sz do
  1693. begin
  1694. place^ := #0;
  1695. Inc(place);
  1696. end;
  1697. end; { of procedure TRegExpr.InsertOperator
  1698. -------------------------------------------------------------- }
  1699. function FindSkippedMetaLen(PStart, PEnd: PRegExprChar): integer; {$IFDEF InlineFuncs}inline;{$ENDIF}
  1700. // find length of initial segment of PStart string consisting
  1701. // entirely of characters not from IsMetaSymbol1.
  1702. begin
  1703. Result := 0;
  1704. while PStart < PEnd do
  1705. begin
  1706. if _IsMetaSymbol1(PStart^) then
  1707. Exit;
  1708. Inc(Result);
  1709. Inc(PStart)
  1710. end;
  1711. end;
  1712. const
  1713. // Flags to be passed up and down.
  1714. flag_HasWidth = 01; // Known never to match nil string.
  1715. flag_Simple = 02; // Simple enough to be OP_STAR/OP_PLUS/OP_BRACES operand.
  1716. flag_SpecStart = 04; // Starts with * or +.
  1717. flag_Worst = 0; // Worst case.
  1718. {$IFDEF UniCode}
  1719. RusRangeLoLow = #$430; // 'а'
  1720. RusRangeLoHigh = #$44F; // 'я'
  1721. RusRangeHiLow = #$410; // 'А'
  1722. RusRangeHiHigh = #$42F; // 'Я'
  1723. {$ELSE}
  1724. RusRangeLoLow = #$E0; // 'а' in cp1251
  1725. RusRangeLoHigh = #$FF; // 'я' in cp1251
  1726. RusRangeHiLow = #$C0; // 'А' in cp1251
  1727. RusRangeHiHigh = #$DF; // 'Я' in cp1251
  1728. {$ENDIF}
  1729. function TRegExpr.FindInCharClass(ABuffer: PRegExprChar; AChar: REChar; AIgnoreCase: boolean): boolean;
  1730. // Buffer contains char pairs: (Kind, Data), where Kind is one of OpKind_ values,
  1731. // and Data depends on Kind
  1732. var
  1733. ch, ch2: REChar;
  1734. N, i: integer;
  1735. begin
  1736. if AIgnoreCase then
  1737. AChar := _UpperCase(AChar);
  1738. repeat
  1739. case ABuffer^ of
  1740. OpKind_End:
  1741. begin
  1742. Result := False;
  1743. Exit;
  1744. end;
  1745. OpKind_Range:
  1746. begin
  1747. Inc(ABuffer);
  1748. ch := ABuffer^;
  1749. Inc(ABuffer);
  1750. ch2 := ABuffer^;
  1751. Inc(ABuffer);
  1752. {
  1753. // if AIgnoreCase, ch, ch2 are upcased in opcode
  1754. if AIgnoreCase then
  1755. begin
  1756. ch := _UpperCase(ch);
  1757. ch2 := _UpperCase(ch2);
  1758. end;
  1759. }
  1760. if (AChar >= ch) and (AChar <= ch2) then
  1761. begin
  1762. Result := True;
  1763. Exit;
  1764. end;
  1765. end;
  1766. OpKind_MetaClass:
  1767. begin
  1768. Inc(ABuffer);
  1769. N := Ord(ABuffer^);
  1770. Inc(ABuffer);
  1771. if CharCheckers[N](AChar) then
  1772. begin
  1773. Result := True;
  1774. Exit
  1775. end;
  1776. end;
  1777. OpKind_Char:
  1778. begin
  1779. Inc(ABuffer);
  1780. N := PLongInt(ABuffer)^;
  1781. Inc(ABuffer, RENumberSz);
  1782. for i := 1 to N do
  1783. begin
  1784. ch := ABuffer^;
  1785. Inc(ABuffer);
  1786. {
  1787. // already upcased in opcode
  1788. if AIgnoreCase then
  1789. ch := _UpperCase(ch);
  1790. }
  1791. if ch = AChar then
  1792. begin
  1793. Result := True;
  1794. Exit;
  1795. end;
  1796. end;
  1797. end;
  1798. else
  1799. Error(reeBadOpcodeInCharClass);
  1800. end;
  1801. until False; // assume that Buffer is ended correctly
  1802. end;
  1803. procedure TRegExpr.GetCharSetFromWordChars(var ARes: TRegExprCharset);
  1804. var
  1805. i: integer;
  1806. ch: REChar;
  1807. begin
  1808. ARes := [];
  1809. for i := 1 to Length(fWordChars) do
  1810. begin
  1811. ch := fWordChars[i];
  1812. {$IFDEF UniCode}
  1813. if Ord(ch) <= $FF then
  1814. {$ENDIF}
  1815. Include(ARes, byte(ch));
  1816. end;
  1817. end;
  1818. procedure TRegExpr.GetCharSetFromSpaceChars(var ARes: TRegExprCharset);
  1819. var
  1820. i: integer;
  1821. ch: REChar;
  1822. begin
  1823. ARes := [];
  1824. for i := 1 to Length(fSpaceChars) do
  1825. begin
  1826. ch := fSpaceChars[i];
  1827. {$IFDEF UniCode}
  1828. if Ord(ch) <= $FF then
  1829. {$ENDIF}
  1830. Include(ARes, byte(ch));
  1831. end;
  1832. end;
  1833. procedure TRegExpr.GetCharSetFromCharClass(ABuffer: PRegExprChar; AIgnoreCase: boolean; var ARes: TRegExprCharset);
  1834. var
  1835. ch, ch2: REChar;
  1836. TempSet: TRegExprCharSet;
  1837. N, i: integer;
  1838. begin
  1839. ARes := [];
  1840. TempSet := [];
  1841. repeat
  1842. case ABuffer^ of
  1843. OpKind_End:
  1844. Exit;
  1845. OpKind_Range:
  1846. begin
  1847. Inc(ABuffer);
  1848. ch := ABuffer^;
  1849. Inc(ABuffer);
  1850. ch2 := ABuffer^;
  1851. Inc(ABuffer);
  1852. for i := Ord(ch) to
  1853. {$IFDEF UniCode} Min(Ord(ch2), $FF) {$ELSE} Ord(ch2) {$ENDIF} do
  1854. begin
  1855. Include(ARes, byte(i));
  1856. if AIgnoreCase then
  1857. Include(ARes, byte(InvertCase(REChar(i))));
  1858. end;
  1859. end;
  1860. OpKind_MetaClass:
  1861. begin
  1862. Inc(ABuffer);
  1863. N := Ord(ABuffer^);
  1864. Inc(ABuffer);
  1865. if N = CheckerIndex_Word then
  1866. begin
  1867. GetCharSetFromWordChars(TempSet);
  1868. ARes := ARes + TempSet;
  1869. end
  1870. else
  1871. if N = CheckerIndex_NotWord then
  1872. begin
  1873. GetCharSetFromWordChars(TempSet);
  1874. ARes := ARes + (RegExprAllSet - TempSet);
  1875. end
  1876. else
  1877. if N = CheckerIndex_Space then
  1878. begin
  1879. GetCharSetFromSpaceChars(TempSet);
  1880. ARes := ARes + TempSet;
  1881. end
  1882. else
  1883. if N = CheckerIndex_NotSpace then
  1884. begin
  1885. GetCharSetFromSpaceChars(TempSet);
  1886. ARes := ARes + (RegExprAllSet - TempSet);
  1887. end
  1888. else
  1889. if N = CheckerIndex_Digit then
  1890. ARes := ARes + RegExprDigitSet
  1891. else
  1892. if N = CheckerIndex_NotDigit then
  1893. ARes := ARes + (RegExprAllSet - RegExprDigitSet)
  1894. else
  1895. if N = CheckerIndex_VertSep then
  1896. ARes := ARes + RegExprLineSeparatorsSet
  1897. else
  1898. if N = CheckerIndex_NotVertSep then
  1899. ARes := ARes + (RegExprAllSet - RegExprLineSeparatorsSet)
  1900. else
  1901. if N = CheckerIndex_HorzSep then
  1902. ARes := ARes + RegExprHorzSeparatorsSet
  1903. else
  1904. if N = CheckerIndex_NotHorzSep then
  1905. ARes := ARes + (RegExprAllSet - RegExprHorzSeparatorsSet)
  1906. else
  1907. if N = CheckerIndex_LowerAZ then
  1908. begin
  1909. if AIgnoreCase then
  1910. ARes := ARes + RegExprAllAzSet
  1911. else
  1912. ARes := ARes + RegExprLowerAzSet;
  1913. end
  1914. else
  1915. if N = CheckerIndex_UpperAZ then
  1916. begin
  1917. if AIgnoreCase then
  1918. ARes := ARes + RegExprAllAzSet
  1919. else
  1920. ARes := ARes + RegExprUpperAzSet;
  1921. end
  1922. else
  1923. Error(reeBadOpcodeInCharClass);
  1924. end;
  1925. OpKind_Char:
  1926. begin
  1927. Inc(ABuffer);
  1928. N := PLongInt(ABuffer)^;
  1929. Inc(ABuffer, RENumberSz);
  1930. for i := 1 to N do
  1931. begin
  1932. ch := ABuffer^;
  1933. Inc(ABuffer);
  1934. {$IFDEF UniCode}
  1935. if Ord(ch) <= $FF then
  1936. {$ENDIF}
  1937. begin
  1938. Include(ARes, byte(ch));
  1939. if AIgnoreCase then
  1940. Include(ARes, byte(InvertCase(ch)));
  1941. end;
  1942. end;
  1943. end;
  1944. else
  1945. Error(reeBadOpcodeInCharClass);
  1946. end;
  1947. until False; // assume that Buffer is ended correctly
  1948. end;
  1949. function TRegExpr.GetModifierG: boolean;
  1950. begin
  1951. Result := fModifiers.G;
  1952. end;
  1953. function TRegExpr.GetModifierI: boolean;
  1954. begin
  1955. Result := fModifiers.I;
  1956. end;
  1957. function TRegExpr.GetModifierM: boolean;
  1958. begin
  1959. Result := fModifiers.M;
  1960. end;
  1961. function TRegExpr.GetModifierR: boolean;
  1962. begin
  1963. Result := fModifiers.R;
  1964. end;
  1965. function TRegExpr.GetModifierS: boolean;
  1966. begin
  1967. Result := fModifiers.S;
  1968. end;
  1969. function TRegExpr.GetModifierX: boolean;
  1970. begin
  1971. Result := fModifiers.X;
  1972. end;
  1973. function TRegExpr.CompileRegExpr(ARegExp: PRegExprChar): boolean;
  1974. // Compile a regular expression into internal code
  1975. // We can't allocate space until we know how big the compiled form will be,
  1976. // but we can't compile it (and thus know how big it is) until we've got a
  1977. // place to put the code. So we cheat: we compile it twice, once with code
  1978. // generation turned off and size counting turned on, and once "for real".
  1979. // This also means that we don't allocate space until we are sure that the
  1980. // thing really will compile successfully, and we never have to move the
  1981. // code and thus invalidate pointers into it. (Note that it has to be in
  1982. // one piece because free() must be able to free it all.)
  1983. // Beware that the optimization-preparation code in here knows about some
  1984. // of the structure of the compiled regexp.
  1985. var
  1986. scan, longest, longestTemp: PRegExprChar;
  1987. Len, LenTemp: integer;
  1988. flags: integer;
  1989. begin
  1990. Result := False; // life too dark
  1991. flags := 0;
  1992. regparse := nil; // for correct error handling
  1993. regexpBegin := ARegExp;
  1994. regExactlyLen := nil;
  1995. ClearInternalIndexes;
  1996. fLastError := reeOk;
  1997. fLastErrorOpcode := TREOp(0);
  1998. try
  1999. if programm <> nil then
  2000. begin
  2001. FreeMem(programm);
  2002. programm := nil;
  2003. end;
  2004. if ARegExp = nil then
  2005. begin
  2006. Error(reeCompNullArgument);
  2007. Exit;
  2008. end;
  2009. fProgModifiers := fModifiers;
  2010. // well, may it's paranoia. I'll check it later... !!!!!!!!
  2011. // First pass: determine size, legality.
  2012. fSecondPass := False;
  2013. fCompModifiers := fModifiers;
  2014. regparse := ARegExp;
  2015. regnpar := 1;
  2016. regsize := 0;
  2017. regcode := @regdummy;
  2018. EmitC(OP_MAGIC);
  2019. if ParseReg(0, flags) = nil then
  2020. Exit;
  2021. // Allocate space.
  2022. GetMem(programm, regsize * SizeOf(REChar));
  2023. // Second pass: emit code.
  2024. fSecondPass := True;
  2025. fCompModifiers := fModifiers;
  2026. regparse := ARegExp;
  2027. regnpar := 1;
  2028. regcode := programm;
  2029. EmitC(OP_MAGIC);
  2030. if ParseReg(0, flags) = nil then
  2031. Exit;
  2032. // Dig out information for optimizations.
  2033. {$IFDEF UseFirstCharSet} // ###0.929
  2034. FirstCharSet := [];
  2035. FillFirstCharSet(programm + REOpSz);
  2036. for Len := 0 to 255 do
  2037. FirstCharArray[Len] := byte(Len) in FirstCharSet;
  2038. {$ENDIF}
  2039. reganchored := #0;
  2040. regmust := nil;
  2041. regmustlen := 0;
  2042. regmustString := '';
  2043. scan := programm + REOpSz; // First OP_BRANCH.
  2044. if PREOp(regnext(scan))^ = OP_EEND then
  2045. begin // Only one top-level choice.
  2046. scan := scan + REOpSz + RENextOffSz;
  2047. // Starting-point info.
  2048. if PREOp(scan)^ = OP_BOL then
  2049. Inc(reganchored);
  2050. // If there's something expensive in the r.e., find the longest
  2051. // literal string that must appear and make it the regmust. Resolve
  2052. // ties in favor of later strings, since the regstart check works
  2053. // with the beginning of the r.e. and avoiding duplication
  2054. // strengthens checking. Not a strong reason, but sufficient in the
  2055. // absence of others.
  2056. if (flags and flag_SpecStart) <> 0 then
  2057. begin
  2058. longest := nil;
  2059. Len := 0;
  2060. while scan <> nil do
  2061. begin
  2062. if PREOp(scan)^ = OP_EXACTLY then
  2063. begin
  2064. longestTemp := scan + REOpSz + RENextOffSz + RENumberSz;
  2065. LenTemp := PLongInt(scan + REOpSz + RENextOffSz)^;
  2066. if LenTemp >= Len then
  2067. begin
  2068. longest := longestTemp;
  2069. Len := LenTemp;
  2070. end;
  2071. end;
  2072. scan := regnext(scan);
  2073. end;
  2074. regmust := longest;
  2075. regmustlen := Len;
  2076. if regmustlen > 1 then // don't use regmust if too short
  2077. SetString(regmustString, regmust, regmustlen);
  2078. end;
  2079. end;
  2080. Result := True;
  2081. finally
  2082. begin
  2083. if not Result then
  2084. InvalidateProgramm;
  2085. regexpBegin := nil;
  2086. regexpIsCompiled := Result; // ###0.944
  2087. end;
  2088. end;
  2089. end; { of function TRegExpr.CompileRegExpr
  2090. -------------------------------------------------------------- }
  2091. procedure TRegExpr.SetUseOsLineEndOnReplace(AValue: boolean);
  2092. begin
  2093. if FUseOsLineEndOnReplace = AValue then
  2094. Exit;
  2095. FUseOsLineEndOnReplace := AValue;
  2096. if FUseOsLineEndOnReplace then
  2097. FReplaceLineEnd := sLineBreak
  2098. else
  2099. FReplaceLineEnd := #10;
  2100. end;
  2101. function TRegExpr.ParseReg(paren: integer; var flagp: integer): PRegExprChar;
  2102. // regular expression, i.e. main body or parenthesized thing
  2103. // Caller must absorb opening parenthesis.
  2104. // Combining parenthesis handling with the base level of regular expression
  2105. // is a trifle forced, but the need to tie the tails of the branches to what
  2106. // follows makes it hard to avoid.
  2107. var
  2108. ret, br, ender: PRegExprChar;
  2109. parno: integer;
  2110. flags: integer;
  2111. SavedModifiers: TRegExprModifiers;
  2112. begin
  2113. flags := 0;
  2114. Result := nil;
  2115. flagp := flag_HasWidth; // Tentatively.
  2116. parno := 0; // eliminate compiler stupid warning
  2117. SavedModifiers := fCompModifiers;
  2118. // Make an OP_OPEN node, if parenthesized.
  2119. if paren <> 0 then
  2120. begin
  2121. if regnpar >= NSUBEXP then
  2122. begin
  2123. Error(reeCompParseRegTooManyBrackets);
  2124. Exit;
  2125. end;
  2126. parno := regnpar;
  2127. Inc(regnpar);
  2128. ret := EmitNode(TREOp(Ord(OP_OPEN) + parno));
  2129. end
  2130. else
  2131. ret := nil;
  2132. // Pick up the branches, linking them together.
  2133. br := ParseBranch(flags);
  2134. if br = nil then
  2135. begin
  2136. Result := nil;
  2137. Exit;
  2138. end;
  2139. if ret <> nil then
  2140. Tail(ret, br) // OP_OPEN -> first.
  2141. else
  2142. ret := br;
  2143. if (flags and flag_HasWidth) = 0 then
  2144. flagp := flagp and not flag_HasWidth;
  2145. flagp := flagp or flags and flag_SpecStart;
  2146. while (regparse^ = '|') do
  2147. begin
  2148. Inc(regparse);
  2149. br := ParseBranch(flags);
  2150. if br = nil then
  2151. begin
  2152. Result := nil;
  2153. Exit;
  2154. end;
  2155. Tail(ret, br); // OP_BRANCH -> OP_BRANCH.
  2156. if (flags and flag_HasWidth) = 0 then
  2157. flagp := flagp and not flag_HasWidth;
  2158. flagp := flagp or flags and flag_SpecStart;
  2159. end;
  2160. // Make a closing node, and hook it on the end.
  2161. if paren <> 0 then
  2162. ender := EmitNode(TREOp(Ord(OP_CLOSE) + parno))
  2163. else
  2164. ender := EmitNode(OP_EEND);
  2165. Tail(ret, ender);
  2166. // Hook the tails of the branches to the closing node.
  2167. br := ret;
  2168. while br <> nil do
  2169. begin
  2170. OpTail(br, ender);
  2171. br := regnext(br);
  2172. end;
  2173. // Check for proper termination.
  2174. if paren <> 0 then
  2175. if regparse^ <> ')' then
  2176. begin
  2177. Error(reeCompParseRegUnmatchedBrackets);
  2178. Exit;
  2179. end
  2180. else
  2181. Inc(regparse); // skip trailing ')'
  2182. if (paren = 0) and (regparse < fRegexEnd) then
  2183. begin
  2184. if regparse^ = ')' then
  2185. Error(reeCompParseRegUnmatchedBrackets2)
  2186. else
  2187. Error(reeCompParseRegJunkOnEnd);
  2188. Exit;
  2189. end;
  2190. fCompModifiers := SavedModifiers; // restore modifiers of parent
  2191. Result := ret;
  2192. end; { of function TRegExpr.ParseReg
  2193. -------------------------------------------------------------- }
  2194. function TRegExpr.ParseBranch(var flagp: integer): PRegExprChar;
  2195. // one alternative of an | operator
  2196. // Implements the concatenation operator.
  2197. var
  2198. ret, chain, latest: PRegExprChar;
  2199. flags: integer;
  2200. begin
  2201. flags := 0;
  2202. flagp := flag_Worst; // Tentatively.
  2203. ret := EmitNode(OP_BRANCH);
  2204. chain := nil;
  2205. while (regparse < fRegexEnd) and (regparse^ <> '|') and (regparse^ <> ')') do
  2206. begin
  2207. latest := ParsePiece(flags);
  2208. if latest = nil then
  2209. begin
  2210. Result := nil;
  2211. Exit;
  2212. end;
  2213. flagp := flagp or flags and flag_HasWidth;
  2214. if chain = nil // First piece.
  2215. then
  2216. flagp := flagp or flags and flag_SpecStart
  2217. else
  2218. Tail(chain, latest);
  2219. chain := latest;
  2220. end;
  2221. if chain = nil // Loop ran zero times.
  2222. then
  2223. EmitNode(OP_NOTHING);
  2224. Result := ret;
  2225. end; { of function TRegExpr.ParseBranch
  2226. -------------------------------------------------------------- }
  2227. function TRegExpr.ParsePiece(var flagp: integer): PRegExprChar;
  2228. // something followed by possible [*+?{]
  2229. // Note that the branching code sequences used for ? and the general cases
  2230. // of * and + and { are somewhat optimized: they use the same OP_NOTHING node as
  2231. // both the endmarker for their branch list and the body of the last branch.
  2232. // It might seem that this node could be dispensed with entirely, but the
  2233. // endmarker role is not redundant.
  2234. function ParseNumber(AStart, AEnd: PRegExprChar): TREBracesArg;
  2235. begin
  2236. Result := 0;
  2237. if AEnd - AStart + 1 > 8 then
  2238. begin // prevent stupid scanning
  2239. Error(reeBRACESArgTooBig);
  2240. Exit;
  2241. end;
  2242. while AStart <= AEnd do
  2243. begin
  2244. Result := Result * 10 + (Ord(AStart^) - Ord('0'));
  2245. Inc(AStart);
  2246. end;
  2247. if (Result > MaxBracesArg) or (Result < 0) then
  2248. begin
  2249. Error(reeBRACESArgTooBig);
  2250. Exit;
  2251. end;
  2252. end;
  2253. var
  2254. TheOp: TREOp;
  2255. NextNode: PRegExprChar;
  2256. procedure EmitComplexBraces(ABracesMin, ABracesMax: TREBracesArg; ANonGreedyOp: boolean); // ###0.940
  2257. {$IFDEF ComplexBraces}
  2258. var
  2259. off: TRENextOff;
  2260. {$ENDIF}
  2261. begin
  2262. {$IFNDEF ComplexBraces}
  2263. Error(reeComplexBracesNotImplemented);
  2264. {$ELSE}
  2265. if ANonGreedyOp then
  2266. TheOp := OP_LOOPNG
  2267. else
  2268. TheOp := OP_LOOP;
  2269. InsertOperator(OP_LOOPENTRY, Result, REOpSz + RENextOffSz);
  2270. NextNode := EmitNode(TheOp);
  2271. if regcode <> @regdummy then
  2272. begin
  2273. off := (Result + REOpSz + RENextOffSz) - (regcode - REOpSz - RENextOffSz);
  2274. // back to Atom after OP_LOOPENTRY
  2275. PREBracesArg(AlignToInt(regcode))^ := ABracesMin;
  2276. Inc(regcode, REBracesArgSz);
  2277. PREBracesArg(AlignToInt(regcode))^ := ABracesMax;
  2278. Inc(regcode, REBracesArgSz);
  2279. PRENextOff(AlignToPtr(regcode))^ := off;
  2280. Inc(regcode, RENextOffSz);
  2281. {$IFDEF DebugSynRegExpr}
  2282. if regcode - programm > regsize then
  2283. raise Exception.Create
  2284. ('TRegExpr.ParsePiece.EmitComplexBraces buffer overrun');
  2285. {$ENDIF}
  2286. end
  2287. else
  2288. Inc(regsize, REBracesArgSz * 2 + RENextOffSz);
  2289. Tail(Result, NextNode); // OP_LOOPENTRY -> OP_LOOP
  2290. if regcode <> @regdummy then
  2291. Tail(Result + REOpSz + RENextOffSz, NextNode); // Atom -> OP_LOOP
  2292. {$ENDIF}
  2293. end;
  2294. procedure EmitSimpleBraces(ABracesMin, ABracesMax: TREBracesArg; ANonGreedyOp: boolean); // ###0.940
  2295. begin
  2296. if ANonGreedyOp // ###0.940
  2297. then
  2298. TheOp := OP_BRACESNG
  2299. else
  2300. TheOp := OP_BRACES;
  2301. InsertOperator(TheOp, Result, REOpSz + RENextOffSz + REBracesArgSz * 2);
  2302. if regcode <> @regdummy then
  2303. begin
  2304. PREBracesArg(AlignToInt(Result + REOpSz + RENextOffSz))^ := ABracesMin;
  2305. PREBracesArg(AlignToInt(Result + REOpSz + RENextOffSz + REBracesArgSz))^ := ABracesMax;
  2306. end;
  2307. end;
  2308. var
  2309. op: REChar;
  2310. NonGreedyOp, NonGreedyCh: boolean; // ###0.940
  2311. flags: integer;
  2312. BracesMin, Bracesmax: TREBracesArg;
  2313. p: PRegExprChar;
  2314. begin
  2315. flags := 0;
  2316. Result := ParseAtom(flags);
  2317. if Result = nil then
  2318. Exit;
  2319. op := regparse^;
  2320. if not ((op = '*') or (op = '+') or (op = '?') or (op = '{')) then
  2321. begin
  2322. flagp := flags;
  2323. Exit;
  2324. end;
  2325. if ((flags and flag_HasWidth) = 0) and (op <> '?') then
  2326. begin
  2327. Error(reePlusStarOperandCouldBeEmpty);
  2328. Exit;
  2329. end;
  2330. case op of
  2331. '*':
  2332. begin
  2333. flagp := flag_Worst or flag_SpecStart;
  2334. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2335. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2336. // ###0.940
  2337. if (flags and flag_Simple) = 0 then
  2338. begin
  2339. if NonGreedyOp // ###0.940
  2340. then
  2341. EmitComplexBraces(0, MaxBracesArg, NonGreedyOp)
  2342. else
  2343. begin // Emit x* as (x&|), where & means "self".
  2344. InsertOperator(OP_BRANCH, Result, REOpSz + RENextOffSz); // Either x
  2345. OpTail(Result, EmitNode(OP_BACK)); // and loop
  2346. OpTail(Result, Result); // back
  2347. Tail(Result, EmitNode(OP_BRANCH)); // or
  2348. Tail(Result, EmitNode(OP_NOTHING)); // nil.
  2349. end
  2350. end
  2351. else
  2352. begin // Simple
  2353. if NonGreedyOp // ###0.940
  2354. then
  2355. TheOp := OP_STARNG
  2356. else
  2357. TheOp := OP_STAR;
  2358. InsertOperator(TheOp, Result, REOpSz + RENextOffSz);
  2359. end;
  2360. if NonGreedyCh // ###0.940
  2361. then
  2362. Inc(regparse); // Skip extra char ('?')
  2363. end; { of case '*' }
  2364. '+':
  2365. begin
  2366. flagp := flag_Worst or flag_SpecStart or flag_HasWidth;
  2367. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2368. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2369. // ###0.940
  2370. if (flags and flag_Simple) = 0 then
  2371. begin
  2372. if NonGreedyOp // ###0.940
  2373. then
  2374. EmitComplexBraces(1, MaxBracesArg, NonGreedyOp)
  2375. else
  2376. begin // Emit x+ as x(&|), where & means "self".
  2377. NextNode := EmitNode(OP_BRANCH); // Either
  2378. Tail(Result, NextNode);
  2379. Tail(EmitNode(OP_BACK), Result); // loop back
  2380. Tail(NextNode, EmitNode(OP_BRANCH)); // or
  2381. Tail(Result, EmitNode(OP_NOTHING)); // nil.
  2382. end
  2383. end
  2384. else
  2385. begin // Simple
  2386. if NonGreedyOp // ###0.940
  2387. then
  2388. TheOp := OP_PLUSNG
  2389. else
  2390. TheOp := OP_PLUS;
  2391. InsertOperator(TheOp, Result, REOpSz + RENextOffSz);
  2392. end;
  2393. if NonGreedyCh // ###0.940
  2394. then
  2395. Inc(regparse); // Skip extra char ('?')
  2396. end; { of case '+' }
  2397. '?':
  2398. begin
  2399. flagp := flag_Worst;
  2400. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2401. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2402. // ###0.940
  2403. if NonGreedyOp then
  2404. begin // ###0.940 // We emit x?? as x{0,1}?
  2405. if (flags and flag_Simple) = 0 then
  2406. EmitComplexBraces(0, 1, NonGreedyOp)
  2407. else
  2408. EmitSimpleBraces(0, 1, NonGreedyOp);
  2409. end
  2410. else
  2411. begin // greedy '?'
  2412. InsertOperator(OP_BRANCH, Result, REOpSz + RENextOffSz); // Either x
  2413. Tail(Result, EmitNode(OP_BRANCH)); // or
  2414. NextNode := EmitNode(OP_NOTHING); // nil.
  2415. Tail(Result, NextNode);
  2416. OpTail(Result, NextNode);
  2417. end;
  2418. if NonGreedyCh // ###0.940
  2419. then
  2420. Inc(regparse); // Skip extra char ('?')
  2421. end; { of case '?' }
  2422. '{':
  2423. begin
  2424. Inc(regparse);
  2425. p := regparse;
  2426. while IsDigitChar(regparse^) do // <min> MUST appear
  2427. Inc(regparse);
  2428. if (regparse^ <> '}') and (regparse^ <> ',') or (p = regparse) then
  2429. begin
  2430. Error(reeIncorrectBraces);
  2431. Exit;
  2432. end;
  2433. BracesMin := ParseNumber(p, regparse - 1);
  2434. if regparse^ = ',' then
  2435. begin
  2436. Inc(regparse);
  2437. p := regparse;
  2438. while IsDigitChar(regparse^) do
  2439. Inc(regparse);
  2440. if regparse^ <> '}' then
  2441. begin
  2442. Error(reeIncorrectBraces);
  2443. Exit;
  2444. end;
  2445. if p = regparse then
  2446. Bracesmax := MaxBracesArg
  2447. else
  2448. Bracesmax := ParseNumber(p, regparse - 1);
  2449. end
  2450. else
  2451. Bracesmax := BracesMin; // {n} == {n,n}
  2452. if BracesMin > Bracesmax then
  2453. begin
  2454. Error(reeBracesMinParamGreaterMax);
  2455. Exit;
  2456. end;
  2457. if BracesMin > 0 then
  2458. flagp := flag_Worst;
  2459. if Bracesmax > 0 then
  2460. flagp := flagp or flag_HasWidth or flag_SpecStart;
  2461. NonGreedyCh := (regparse + 1)^ = '?'; // ###0.940
  2462. NonGreedyOp := NonGreedyCh or not fCompModifiers.G;
  2463. // ###0.940
  2464. if (flags and flag_Simple) <> 0 then
  2465. EmitSimpleBraces(BracesMin, Bracesmax, NonGreedyOp)
  2466. else
  2467. EmitComplexBraces(BracesMin, Bracesmax, NonGreedyOp);
  2468. if NonGreedyCh // ###0.940
  2469. then
  2470. Inc(regparse); // Skip extra char '?'
  2471. end; // of case '{'
  2472. // else // here we can't be
  2473. end; { of case op }
  2474. Inc(regparse);
  2475. op := regparse^;
  2476. if (op = '*') or (op = '+') or (op = '?') or (op = '{') then
  2477. Error(reeNestedSQP);
  2478. end; { of function TRegExpr.ParsePiece
  2479. -------------------------------------------------------------- }
  2480. function TRegExpr.HexDig(Ch: REChar): integer;
  2481. begin
  2482. case Ch of
  2483. '0' .. '9':
  2484. Result := Ord(Ch) - Ord('0');
  2485. 'a' .. 'f':
  2486. Result := Ord(Ch) - Ord('a') + 10;
  2487. 'A' .. 'F':
  2488. Result := Ord(Ch) - Ord('A') + 10;
  2489. else
  2490. begin
  2491. Result := 0;
  2492. Error(reeBadHexDigit);
  2493. end;
  2494. end;
  2495. end;
  2496. function TRegExpr.UnQuoteChar(var APtr: PRegExprChar): REChar;
  2497. var
  2498. Ch: REChar;
  2499. begin
  2500. case APtr^ of
  2501. 't':
  2502. Result := #$9; // \t => tab (HT/TAB)
  2503. 'n':
  2504. Result := #$a; // \n => newline (NL)
  2505. 'r':
  2506. Result := #$d; // \r => carriage return (CR)
  2507. 'f':
  2508. Result := #$c; // \f => form feed (FF)
  2509. 'a':
  2510. Result := #$7; // \a => alarm (bell) (BEL)
  2511. 'e':
  2512. Result := #$1b; // \e => escape (ESC)
  2513. 'c':
  2514. begin // \cK => code for Ctrl+K
  2515. Inc(APtr);
  2516. if APtr >= fRegexEnd then
  2517. Error(reeNoLetterAfterBSlashC);
  2518. Ch := APtr^;
  2519. case Ch of
  2520. 'a' .. 'z':
  2521. Result := REChar(Ord(Ch) - Ord('a') + 1);
  2522. 'A' .. 'Z':
  2523. Result := REChar(Ord(Ch) - Ord('A') + 1);
  2524. else
  2525. Error(reeNoLetterAfterBSlashC);
  2526. end;
  2527. end;
  2528. 'x':
  2529. begin // \x: hex char
  2530. Result := #0;
  2531. Inc(APtr);
  2532. if APtr >= fRegexEnd then
  2533. begin
  2534. Error(reeNoHexCodeAfterBSlashX);
  2535. Exit;
  2536. end;
  2537. if APtr^ = '{' then
  2538. begin // \x{nnnn} //###0.936
  2539. repeat
  2540. Inc(APtr);
  2541. if APtr >= fRegexEnd then
  2542. begin
  2543. Error(reeNoHexCodeAfterBSlashX);
  2544. Exit;
  2545. end;
  2546. if APtr^ <> '}' then
  2547. begin
  2548. if (Ord(Result) ShR (SizeOf(REChar) * 8 - 4)) and $F <> 0 then
  2549. begin
  2550. Error(reeHexCodeAfterBSlashXTooBig);
  2551. Exit;
  2552. end;
  2553. Result := REChar((Ord(Result) ShL 4) or HexDig(APtr^));
  2554. // HexDig will cause Error if bad hex digit found
  2555. end
  2556. else
  2557. Break;
  2558. until False;
  2559. end
  2560. else
  2561. begin
  2562. Result := REChar(HexDig(APtr^));
  2563. // HexDig will cause Error if bad hex digit found
  2564. Inc(APtr);
  2565. if APtr >= fRegexEnd then
  2566. begin
  2567. Error(reeNoHexCodeAfterBSlashX);
  2568. Exit;
  2569. end;
  2570. Result := REChar((Ord(Result) ShL 4) or HexDig(APtr^));
  2571. // HexDig will cause Error if bad hex digit found
  2572. end;
  2573. end;
  2574. else
  2575. Result := APtr^;
  2576. end;
  2577. end;
  2578. function TRegExpr.ParseAtom(var flagp: integer): PRegExprChar;
  2579. // the lowest level
  2580. // Optimization: gobbles an entire sequence of ordinary characters so that
  2581. // it can turn them into a single node, which is smaller to store and
  2582. // faster to run. Backslashed characters are exceptions, each becoming a
  2583. // separate node; the code is simpler that way and it's not worth fixing.
  2584. var
  2585. ret: PRegExprChar;
  2586. RangeBeg, RangeEnd: REChar;
  2587. CanBeRange: boolean;
  2588. AddrOfLen: PLongInt;
  2589. procedure EmitExactly(Ch: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  2590. begin
  2591. if fCompModifiers.I then
  2592. ret := EmitNode(OP_EXACTLYCI)
  2593. else
  2594. ret := EmitNode(OP_EXACTLY);
  2595. EmitInt(1);
  2596. EmitC(Ch);
  2597. flagp := flagp or flag_HasWidth or flag_Simple;
  2598. end;
  2599. procedure EmitRangeChar(Ch: REChar; AStartOfRange: boolean); {$IFDEF InlineFuncs}inline;{$ENDIF}
  2600. begin
  2601. CanBeRange := AStartOfRange;
  2602. if fCompModifiers.I then
  2603. Ch := _UpperCase(Ch);
  2604. if AStartOfRange then
  2605. begin
  2606. AddrOfLen := nil;
  2607. RangeBeg := Ch;
  2608. end
  2609. else
  2610. begin
  2611. if AddrOfLen = nil then
  2612. begin
  2613. EmitC(OpKind_Char);
  2614. Pointer(AddrOfLen) := regcode;
  2615. EmitInt(0);
  2616. end;
  2617. Inc(AddrOfLen^);
  2618. EmitC(Ch);
  2619. end;
  2620. end;
  2621. procedure EmitRangePacked(ch1, ch2: REChar); {$IFDEF InlineFuncs}inline;{$ENDIF}
  2622. var
  2623. ChkIndex: integer;
  2624. begin
  2625. AddrOfLen := nil;
  2626. CanBeRange := False;
  2627. if fCompModifiers.I then
  2628. begin
  2629. ch1 := _UpperCase(ch1);
  2630. ch2 := _UpperCase(ch2);
  2631. end;
  2632. for ChkIndex := Low(CharCheckerInfos) to High(CharCheckerInfos) do
  2633. if (CharCheckerInfos[ChkIndex].CharBegin = ch1) and
  2634. (CharCheckerInfos[ChkIndex].CharEnd = ch2) then
  2635. begin
  2636. EmitC(OpKind_MetaClass);
  2637. EmitC(REChar(CharCheckerInfos[ChkIndex].CheckerIndex));
  2638. Exit;
  2639. end;
  2640. EmitC(OpKind_Range);
  2641. EmitC(ch1);
  2642. EmitC(ch2);
  2643. end;
  2644. var
  2645. flags: integer;
  2646. Len: integer;
  2647. SavedPtr: PRegExprChar;
  2648. EnderChar, TempChar: REChar;
  2649. begin
  2650. Result := nil;
  2651. flags := 0;
  2652. flagp := flag_Worst;
  2653. AddrOfLen := nil;
  2654. Inc(regparse);
  2655. case (regparse - 1)^ of
  2656. '^':
  2657. if not fCompModifiers.M or
  2658. ((fLineSeparators = '') and not fLinePairedSeparatorAssigned) then
  2659. ret := EmitNode(OP_BOL)
  2660. else
  2661. ret := EmitNode(OP_BOLML);
  2662. '$':
  2663. if not fCompModifiers.M or
  2664. ((fLineSeparators = '') and not fLinePairedSeparatorAssigned) then
  2665. ret := EmitNode(OP_EOL)
  2666. else
  2667. ret := EmitNode(OP_EOLML);
  2668. '.':
  2669. if fCompModifiers.S then
  2670. begin
  2671. ret := EmitNode(OP_ANY);
  2672. flagp := flagp or flag_HasWidth or flag_Simple;
  2673. end
  2674. else
  2675. begin // not /s, so emit [^:LineSeparators:]
  2676. ret := EmitNode(OP_ANYML);
  2677. flagp := flagp or flag_HasWidth; // not so simple ;)
  2678. end;
  2679. '[':
  2680. begin
  2681. if regparse^ = '^' then
  2682. begin // Complement of range.
  2683. if fCompModifiers.I then
  2684. ret := EmitNode(OP_ANYBUTCI)
  2685. else
  2686. ret := EmitNode(OP_ANYBUT);
  2687. Inc(regparse);
  2688. end
  2689. else if fCompModifiers.I then
  2690. ret := EmitNode(OP_ANYOFCI)
  2691. else
  2692. ret := EmitNode(OP_ANYOF);
  2693. CanBeRange := False;
  2694. if regparse^ = ']' then
  2695. begin
  2696. // first ']' inside [] treated as simple char, no need to check '['
  2697. EmitRangeChar(regparse^, (regparse + 1)^ = '-');
  2698. Inc(regparse);
  2699. end;
  2700. while (regparse < fRegexEnd) and (regparse^ <> ']') do
  2701. begin
  2702. if (regparse^ = '-') and ((regparse + 1) < fRegexEnd) and
  2703. ((regparse + 1)^ <> ']') and CanBeRange then
  2704. begin
  2705. Inc(regparse);
  2706. RangeEnd := regparse^;
  2707. if RangeEnd = EscChar then
  2708. begin
  2709. if _IsMetaChar((regparse + 1)^) then
  2710. begin
  2711. Error(reeMetaCharAfterMinusInRange);
  2712. Exit;
  2713. end;
  2714. Inc(regparse);
  2715. RangeEnd := UnQuoteChar(regparse);
  2716. end;
  2717. // special handling for Russian range a-YA, add 2 ranges: a-ya and A-YA
  2718. if fCompModifiers.R and
  2719. (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeHiHigh) then
  2720. begin
  2721. EmitRangePacked(RusRangeLoLow, RusRangeLoHigh);
  2722. EmitRangePacked(RusRangeHiLow, RusRangeHiHigh);
  2723. end
  2724. else
  2725. begin // standard r.e. handling
  2726. if RangeBeg > RangeEnd then
  2727. begin
  2728. Error(reeInvalidRange);
  2729. Exit;
  2730. end;
  2731. EmitRangePacked(RangeBeg, RangeEnd);
  2732. end;
  2733. Inc(regparse);
  2734. end
  2735. else
  2736. begin
  2737. if regparse^ = EscChar then
  2738. begin
  2739. Inc(regparse);
  2740. if regparse >= fRegexEnd then
  2741. begin
  2742. Error(reeParseAtomTrailingBackSlash);
  2743. Exit;
  2744. end;
  2745. if _IsMetaChar(regparse^) then
  2746. begin
  2747. AddrOfLen := nil;
  2748. CanBeRange := False;
  2749. EmitC(OpKind_MetaClass);
  2750. case regparse^ of
  2751. 'w':
  2752. EmitC(REChar(CheckerIndex_Word));
  2753. 'W':
  2754. EmitC(REChar(CheckerIndex_NotWord));
  2755. 's':
  2756. EmitC(REChar(CheckerIndex_Space));
  2757. 'S':
  2758. EmitC(REChar(CheckerIndex_NotSpace));
  2759. 'd':
  2760. EmitC(REChar(CheckerIndex_Digit));
  2761. 'D':
  2762. EmitC(REChar(CheckerIndex_NotDigit));
  2763. 'v':
  2764. EmitC(REChar(CheckerIndex_VertSep));
  2765. 'V':
  2766. EmitC(REChar(CheckerIndex_NotVertSep));
  2767. 'h':
  2768. EmitC(REChar(CheckerIndex_HorzSep));
  2769. 'H':
  2770. EmitC(REChar(CheckerIndex_NotHorzSep));
  2771. else
  2772. Error(reeBadOpcodeInCharClass);
  2773. end;
  2774. end
  2775. else
  2776. begin
  2777. TempChar := UnQuoteChar(regparse);
  2778. EmitRangeChar(TempChar, (regparse + 1)^ = '-');
  2779. end;
  2780. end
  2781. else
  2782. begin
  2783. EmitRangeChar(regparse^, (regparse + 1)^ = '-');
  2784. end;
  2785. Inc(regparse);
  2786. end;
  2787. end; { of while }
  2788. AddrOfLen := nil;
  2789. CanBeRange := False;
  2790. EmitC(OpKind_End);
  2791. if regparse^ <> ']' then
  2792. begin
  2793. Error(reeUnmatchedSqBrackets);
  2794. Exit;
  2795. end;
  2796. Inc(regparse);
  2797. flagp := flagp or flag_HasWidth or flag_Simple;
  2798. end;
  2799. '(':
  2800. begin
  2801. if regparse^ = '?' then
  2802. begin
  2803. // check for non-capturing group: (?:text)
  2804. if (regparse + 1)^ = ':' then
  2805. begin
  2806. Inc(regparse, 2);
  2807. ret := ParseReg(1, flags);
  2808. if ret = nil then
  2809. begin
  2810. Result := nil;
  2811. Exit;
  2812. end;
  2813. flagp := flagp or flags and (flag_HasWidth or flag_SpecStart);
  2814. end
  2815. else
  2816. // check for extended Perl syntax : (?..)
  2817. if (regparse + 1)^ = '#' then
  2818. begin // (?#comment)
  2819. Inc(regparse, 2); // find closing ')'
  2820. while (regparse < fRegexEnd) and (regparse^ <> ')') do
  2821. Inc(regparse);
  2822. if regparse^ <> ')' then
  2823. begin
  2824. Error(reeUnclosedComment);
  2825. Exit;
  2826. end;
  2827. Inc(regparse); // skip ')'
  2828. ret := EmitNode(OP_COMMENT); // comment
  2829. end
  2830. else
  2831. begin // modifiers ?
  2832. Inc(regparse); // skip '?'
  2833. SavedPtr := regparse;
  2834. while (regparse < fRegexEnd) and (regparse^ <> ')') do
  2835. Inc(regparse);
  2836. if (regparse^ <> ')') or
  2837. not ParseModifiers(SavedPtr, regparse - SavedPtr, fCompModifiers) then
  2838. begin
  2839. Error(reeUnrecognizedModifier);
  2840. Exit;
  2841. end;
  2842. Inc(regparse); // skip ')'
  2843. ret := EmitNode(OP_COMMENT); // comment
  2844. // Error (reeQPSBFollowsNothing);
  2845. // Exit;
  2846. end;
  2847. end
  2848. else
  2849. begin
  2850. // normal (capturing) group
  2851. if fSecondPass then
  2852. // must skip this block for one of passes, to not double groups count
  2853. if GrpCount < NSUBEXP - 1 then
  2854. begin
  2855. Inc(GrpCount);
  2856. GrpIndexes[GrpCount] := regnpar;
  2857. end;
  2858. ret := ParseReg(1, flags);
  2859. if ret = nil then
  2860. begin
  2861. Result := nil;
  2862. Exit;
  2863. end;
  2864. flagp := flagp or flags and (flag_HasWidth or flag_SpecStart);
  2865. end;
  2866. end;
  2867. '|', ')':
  2868. begin // Supposed to be caught earlier.
  2869. Error(reeInternalUrp);
  2870. Exit;
  2871. end;
  2872. '?', '+', '*':
  2873. begin
  2874. Error(reeQPSBFollowsNothing);
  2875. Exit;
  2876. end;
  2877. EscChar:
  2878. begin
  2879. if regparse >= fRegexEnd then
  2880. begin
  2881. Error(reeTrailingBackSlash);
  2882. Exit;
  2883. end;
  2884. case regparse^ of // r.e.extensions
  2885. 'b':
  2886. ret := EmitNode(OP_BOUND); // ###0.943
  2887. 'B':
  2888. ret := EmitNode(OP_NOTBOUND); // ###0.943
  2889. 'A':
  2890. ret := EmitNode(OP_BOL); // ###0.941
  2891. 'Z':
  2892. ret := EmitNode(OP_EOL); // ###0.941
  2893. 'd':
  2894. begin // r.e.extension - any digit ('0' .. '9')
  2895. ret := EmitNode(OP_ANYDIGIT);
  2896. flagp := flagp or flag_HasWidth or flag_Simple;
  2897. end;
  2898. 'D':
  2899. begin // r.e.extension - not digit ('0' .. '9')
  2900. ret := EmitNode(OP_NOTDIGIT);
  2901. flagp := flagp or flag_HasWidth or flag_Simple;
  2902. end;
  2903. 's':
  2904. begin // r.e.extension - any space char
  2905. ret := EmitNode(OP_ANYSPACE);
  2906. flagp := flagp or flag_HasWidth or flag_Simple;
  2907. end;
  2908. 'S':
  2909. begin // r.e.extension - not space char
  2910. ret := EmitNode(OP_NOTSPACE);
  2911. flagp := flagp or flag_HasWidth or flag_Simple;
  2912. end;
  2913. 'w':
  2914. begin // r.e.extension - any english char / digit / '_'
  2915. ret := EmitNode(OP_ANYLETTER);
  2916. flagp := flagp or flag_HasWidth or flag_Simple;
  2917. end;
  2918. 'W':
  2919. begin // r.e.extension - not english char / digit / '_'
  2920. ret := EmitNode(OP_NOTLETTER);
  2921. flagp := flagp or flag_HasWidth or flag_Simple;
  2922. end;
  2923. 'v':
  2924. begin
  2925. ret := EmitNode(OP_ANYVERTSEP);
  2926. flagp := flagp or flag_HasWidth or flag_Simple;
  2927. end;
  2928. 'V':
  2929. begin
  2930. ret := EmitNode(OP_NOTVERTSEP);
  2931. flagp := flagp or flag_HasWidth or flag_Simple;
  2932. end;
  2933. 'h':
  2934. begin
  2935. ret := EmitNode(OP_ANYHORZSEP);
  2936. flagp := flagp or flag_HasWidth or flag_Simple;
  2937. end;
  2938. 'H':
  2939. begin
  2940. ret := EmitNode(OP_NOTHORZSEP);
  2941. flagp := flagp or flag_HasWidth or flag_Simple;
  2942. end;
  2943. '1' .. '9':
  2944. begin // ###0.936
  2945. if fCompModifiers.I then
  2946. ret := EmitNode(OP_BSUBEXPCI)
  2947. else
  2948. ret := EmitNode(OP_BSUBEXP);
  2949. EmitC(REChar(Ord(regparse^) - Ord('0')));
  2950. flagp := flagp or flag_HasWidth or flag_Simple;
  2951. end;
  2952. else
  2953. EmitExactly(UnQuoteChar(regparse));
  2954. end; { of case }
  2955. Inc(regparse);
  2956. end;
  2957. else
  2958. begin
  2959. Dec(regparse);
  2960. if fCompModifiers.X and // check for eXtended syntax
  2961. ((regparse^ = '#') or IsIgnoredChar(regparse^)) then
  2962. begin // ###0.941 \x
  2963. if regparse^ = '#' then
  2964. begin // Skip eXtended comment
  2965. // find comment terminator (group of \n and/or \r)
  2966. while (regparse < fRegexEnd) and (regparse^ <> #$d) and
  2967. (regparse^ <> #$a) do
  2968. Inc(regparse);
  2969. while (regparse^ = #$d) or (regparse^ = #$a)
  2970. // skip comment terminator
  2971. do
  2972. Inc(regparse);
  2973. // attempt to support different type of line separators
  2974. end
  2975. else
  2976. begin // Skip the blanks!
  2977. while IsIgnoredChar(regparse^) do
  2978. Inc(regparse);
  2979. end;
  2980. ret := EmitNode(OP_COMMENT); // comment
  2981. end
  2982. else
  2983. begin
  2984. Len := FindSkippedMetaLen(regparse, fRegexEnd);
  2985. if Len <= 0 then
  2986. if regparse^ <> '{' then
  2987. begin
  2988. Error(reeRarseAtomInternalDisaster);
  2989. Exit;
  2990. end
  2991. else
  2992. Len := FindSkippedMetaLen(regparse + 1, fRegexEnd) + 1;
  2993. // bad {n,m} - compile as EXACTLY
  2994. EnderChar := (regparse + Len)^;
  2995. if (Len > 1) and ((EnderChar = '*') or (EnderChar = '+') or (EnderChar = '?') or (EnderChar = '{')) then
  2996. Dec(Len); // back off clear of ?+*{ operand.
  2997. flagp := flagp or flag_HasWidth;
  2998. if Len = 1 then
  2999. flagp := flagp or flag_Simple;
  3000. if fCompModifiers.I then
  3001. ret := EmitNode(OP_EXACTLYCI)
  3002. else
  3003. ret := EmitNode(OP_EXACTLY);
  3004. EmitInt(0);
  3005. while (Len > 0) and ((not fCompModifiers.X) or (regparse^ <> '#')) do
  3006. begin
  3007. if not fCompModifiers.X or not IsIgnoredChar(regparse^) then
  3008. begin
  3009. EmitC(regparse^);
  3010. if regcode <> @regdummy then
  3011. Inc(regExactlyLen^);
  3012. end;
  3013. Inc(regparse);
  3014. Dec(Len);
  3015. end;
  3016. end; { of if not comment }
  3017. end; { of case else }
  3018. end; { of case }
  3019. Result := ret;
  3020. end; { of function TRegExpr.ParseAtom
  3021. -------------------------------------------------------------- }
  3022. function TRegExpr.GetCompilerErrorPos: PtrInt;
  3023. begin
  3024. Result := 0;
  3025. if (regexpBegin = nil) or (regparse = nil) then
  3026. Exit; // not in compiling mode ?
  3027. Result := regparse - regexpBegin;
  3028. end; { of function TRegExpr.GetCompilerErrorPos
  3029. -------------------------------------------------------------- }
  3030. { ============================================================= }
  3031. { ===================== Matching section ====================== }
  3032. { ============================================================= }
  3033. function TRegExpr.regrepeat(p: PRegExprChar; AMax: integer): integer;
  3034. // repeatedly match something simple, report how many
  3035. var
  3036. scan: PRegExprChar;
  3037. opnd: PRegExprChar;
  3038. TheMax, NLen: integer;
  3039. InvChar: REChar; // ###0.931
  3040. GrpStart, GrpEnd: PRegExprChar; // ###0.936
  3041. ArrayIndex: integer;
  3042. begin
  3043. Result := 0;
  3044. scan := reginput;
  3045. opnd := p + REOpSz + RENextOffSz; // OPERAND
  3046. TheMax := fInputEnd - scan;
  3047. if TheMax > AMax then
  3048. TheMax := AMax;
  3049. case PREOp(p)^ of
  3050. OP_ANY:
  3051. begin
  3052. // note - OP_ANYML cannot be proceeded in regrepeat because can skip
  3053. // more than one char at once
  3054. Result := TheMax;
  3055. Inc(scan, Result);
  3056. end;
  3057. OP_EXACTLY:
  3058. begin // in opnd can be only ONE char !!!
  3059. NLen := PLongInt(opnd)^;
  3060. if TheMax > NLen then
  3061. TheMax := NLen;
  3062. Inc(opnd, RENumberSz);
  3063. while (Result < TheMax) and (opnd^ = scan^) do
  3064. begin
  3065. Inc(Result);
  3066. Inc(scan);
  3067. end;
  3068. end;
  3069. OP_EXACTLYCI:
  3070. begin // in opnd can be only ONE char !!!
  3071. NLen := PLongInt(opnd)^;
  3072. if TheMax > NLen then
  3073. TheMax := NLen;
  3074. Inc(opnd, RENumberSz);
  3075. while (Result < TheMax) and (opnd^ = scan^) do
  3076. begin // prevent unneeded InvertCase //###0.931
  3077. Inc(Result);
  3078. Inc(scan);
  3079. end;
  3080. if Result < TheMax then
  3081. begin // ###0.931
  3082. InvChar := InvertCase(opnd^); // store in register
  3083. while (Result < TheMax) and ((opnd^ = scan^) or (InvChar = scan^)) do
  3084. begin
  3085. Inc(Result);
  3086. Inc(scan);
  3087. end;
  3088. end;
  3089. end;
  3090. OP_BSUBEXP:
  3091. begin // ###0.936
  3092. ArrayIndex := GrpIndexes[Ord(opnd^)];
  3093. if ArrayIndex < 0 then
  3094. Exit;
  3095. GrpStart := startp[ArrayIndex];
  3096. if GrpStart = nil then
  3097. Exit;
  3098. GrpEnd := endp[ArrayIndex];
  3099. if GrpEnd = nil then
  3100. Exit;
  3101. repeat
  3102. opnd := GrpStart;
  3103. while opnd < GrpEnd do
  3104. begin
  3105. if (scan >= fInputEnd) or (scan^ <> opnd^) then
  3106. Exit;
  3107. Inc(scan);
  3108. Inc(opnd);
  3109. end;
  3110. Inc(Result);
  3111. reginput := scan;
  3112. until Result >= AMax;
  3113. end;
  3114. OP_BSUBEXPCI:
  3115. begin // ###0.936
  3116. ArrayIndex := GrpIndexes[Ord(opnd^)];
  3117. if ArrayIndex < 0 then
  3118. Exit;
  3119. GrpStart := startp[ArrayIndex];
  3120. if GrpStart = nil then
  3121. Exit;
  3122. GrpEnd := endp[ArrayIndex];
  3123. if GrpEnd = nil then
  3124. Exit;
  3125. repeat
  3126. opnd := GrpStart;
  3127. while opnd < GrpEnd do
  3128. begin
  3129. if (scan >= fInputEnd) or
  3130. ((scan^ <> opnd^) and (scan^ <> InvertCase(opnd^))) then
  3131. Exit;
  3132. Inc(scan);
  3133. Inc(opnd);
  3134. end;
  3135. Inc(Result);
  3136. reginput := scan;
  3137. until Result >= AMax;
  3138. end;
  3139. OP_ANYDIGIT:
  3140. while (Result < TheMax) and IsDigitChar(scan^) do
  3141. begin
  3142. Inc(Result);
  3143. Inc(scan);
  3144. end;
  3145. OP_NOTDIGIT:
  3146. while (Result < TheMax) and not IsDigitChar(scan^) do
  3147. begin
  3148. Inc(Result);
  3149. Inc(scan);
  3150. end;
  3151. OP_ANYLETTER:
  3152. while (Result < TheMax) and IsWordChar(scan^) do // ###0.940
  3153. begin
  3154. Inc(Result);
  3155. Inc(scan);
  3156. end;
  3157. OP_NOTLETTER:
  3158. while (Result < TheMax) and not IsWordChar(scan^) do // ###0.940
  3159. begin
  3160. Inc(Result);
  3161. Inc(scan);
  3162. end;
  3163. OP_ANYSPACE:
  3164. while (Result < TheMax) and IsSpaceChar(scan^) do
  3165. begin
  3166. Inc(Result);
  3167. Inc(scan);
  3168. end;
  3169. OP_NOTSPACE:
  3170. while (Result < TheMax) and not IsSpaceChar(scan^) do
  3171. begin
  3172. Inc(Result);
  3173. Inc(scan);
  3174. end;
  3175. OP_ANYVERTSEP:
  3176. while (Result < TheMax) and IsLineSeparator(scan^) do
  3177. begin
  3178. Inc(Result);
  3179. Inc(scan);
  3180. end;
  3181. OP_NOTVERTSEP:
  3182. while (Result < TheMax) and not IsLineSeparator(scan^) do
  3183. begin
  3184. Inc(Result);
  3185. Inc(scan);
  3186. end;
  3187. OP_ANYHORZSEP:
  3188. while (Result < TheMax) and IsHorzSeparator(scan^) do
  3189. begin
  3190. Inc(Result);
  3191. Inc(scan);
  3192. end;
  3193. OP_NOTHORZSEP:
  3194. while (Result < TheMax) and not IsHorzSeparator(scan^) do
  3195. begin
  3196. Inc(Result);
  3197. Inc(scan);
  3198. end;
  3199. OP_ANYOF:
  3200. while (Result < TheMax) and FindInCharClass(opnd, scan^, False) do
  3201. begin
  3202. Inc(Result);
  3203. Inc(scan);
  3204. end;
  3205. OP_ANYBUT:
  3206. while (Result < TheMax) and not FindInCharClass(opnd, scan^, False) do
  3207. begin
  3208. Inc(Result);
  3209. Inc(scan);
  3210. end;
  3211. OP_ANYOFCI:
  3212. while (Result < TheMax) and FindInCharClass(opnd, scan^, True) do
  3213. begin
  3214. Inc(Result);
  3215. Inc(scan);
  3216. end;
  3217. OP_ANYBUTCI:
  3218. while (Result < TheMax) and not FindInCharClass(opnd, scan^, True) do
  3219. begin
  3220. Inc(Result);
  3221. Inc(scan);
  3222. end;
  3223. else
  3224. begin // Oh dear. Called inappropriately.
  3225. Result := 0; // Best compromise.
  3226. Error(reeRegRepeatCalledInappropriately);
  3227. Exit;
  3228. end;
  3229. end; { of case }
  3230. reginput := scan;
  3231. end; { of function TRegExpr.regrepeat
  3232. -------------------------------------------------------------- }
  3233. function TRegExpr.regnext(p: PRegExprChar): PRegExprChar;
  3234. // dig the "next" pointer out of a node
  3235. var
  3236. offset: TRENextOff;
  3237. begin
  3238. if p = @regdummy then
  3239. begin
  3240. Result := nil;
  3241. Exit;
  3242. end;
  3243. offset := PRENextOff(AlignToPtr(p + REOpSz))^; // ###0.933 inlined NEXT
  3244. if offset = 0 then
  3245. Result := nil
  3246. else
  3247. Result := p + offset;
  3248. end; { of function TRegExpr.regnext
  3249. -------------------------------------------------------------- }
  3250. function TRegExpr.MatchPrim(prog: PRegExprChar): boolean;
  3251. // recursively matching routine
  3252. // Conceptually the strategy is simple: check to see whether the current
  3253. // node matches, call self recursively to see whether the rest matches,
  3254. // and then act accordingly. In practice we make some effort to avoid
  3255. // recursion, in particular by going through "ordinary" nodes (that don't
  3256. // need to know whether the rest of the match failed) by a loop instead of
  3257. // by recursion.
  3258. var
  3259. scan: PRegExprChar; // Current node.
  3260. next: PRegExprChar; // Next node.
  3261. Len: PtrInt;
  3262. opnd: PRegExprChar;
  3263. no: integer;
  3264. save: PRegExprChar;
  3265. nextch: REChar;
  3266. BracesMin, Bracesmax: integer;
  3267. // we use integer instead of TREBracesArg for better support */+
  3268. {$IFDEF ComplexBraces}
  3269. SavedLoopStack: TRegExprLoopStack; // :(( very bad for recursion
  3270. SavedLoopStackIdx: integer; // ###0.925
  3271. {$ENDIF}
  3272. bound1, bound2: boolean;
  3273. begin
  3274. Result := False;
  3275. {$IFDEF ComplexBraces}
  3276. SavedLoopStack:=Default(TRegExprLoopStack);
  3277. SavedLoopStackIdx:=0;
  3278. {$ENDIF}
  3279. scan := prog;
  3280. while scan <> nil do
  3281. begin
  3282. Len := PRENextOff(AlignToPtr(scan + 1))^; // ###0.932 inlined regnext
  3283. if Len = 0 then
  3284. next := nil
  3285. else
  3286. next := scan + Len;
  3287. case scan^ of
  3288. OP_NOTBOUND,
  3289. OP_BOUND:
  3290. begin
  3291. bound1 := (reginput = fInputStart) or not IsWordChar((reginput - 1)^);
  3292. bound2 := (reginput = fInputEnd) or not IsWordChar(reginput^);
  3293. if (scan^ = OP_BOUND) xor (bound1 <> bound2) then
  3294. Exit;
  3295. end;
  3296. OP_BOL:
  3297. begin
  3298. if reginput <> fInputStart then
  3299. Exit;
  3300. end;
  3301. OP_EOL:
  3302. begin
  3303. if reginput < fInputEnd then
  3304. Exit;
  3305. end;
  3306. OP_BOLML:
  3307. if reginput > fInputStart then
  3308. begin
  3309. nextch := (reginput - 1)^;
  3310. if (nextch <> fLinePairedSeparatorTail) or
  3311. ((reginput - 1) <= fInputStart) or
  3312. ((reginput - 2)^ <> fLinePairedSeparatorHead) then
  3313. begin
  3314. if (nextch = fLinePairedSeparatorHead) and
  3315. (reginput^ = fLinePairedSeparatorTail) then
  3316. Exit; // don't stop between paired separator
  3317. if not IsCustomLineSeparator(nextch) then
  3318. Exit;
  3319. end;
  3320. end;
  3321. OP_EOLML:
  3322. if reginput < fInputEnd then
  3323. begin
  3324. nextch := reginput^;
  3325. if (nextch <> fLinePairedSeparatorHead) or
  3326. ((reginput + 1)^ <> fLinePairedSeparatorTail) then
  3327. begin
  3328. if (nextch = fLinePairedSeparatorTail) and (reginput > fInputStart)
  3329. and ((reginput - 1)^ = fLinePairedSeparatorHead) then
  3330. Exit; // don't stop between paired separator
  3331. if not IsCustomLineSeparator(nextch) then
  3332. Exit;
  3333. end;
  3334. end;
  3335. OP_ANY:
  3336. begin
  3337. if reginput = fInputEnd then
  3338. Exit;
  3339. Inc(reginput);
  3340. end;
  3341. OP_ANYML:
  3342. begin // ###0.941
  3343. if (reginput = fInputEnd) or
  3344. ((reginput^ = fLinePairedSeparatorHead) and
  3345. ((reginput + 1)^ = fLinePairedSeparatorTail)) or
  3346. IsCustomLineSeparator(reginput^)
  3347. then
  3348. Exit;
  3349. Inc(reginput);
  3350. end;
  3351. OP_ANYDIGIT:
  3352. begin
  3353. if (reginput = fInputEnd) or not IsDigitChar(reginput^) then
  3354. Exit;
  3355. Inc(reginput);
  3356. end;
  3357. OP_NOTDIGIT:
  3358. begin
  3359. if (reginput = fInputEnd) or IsDigitChar(reginput^) then
  3360. Exit;
  3361. Inc(reginput);
  3362. end;
  3363. OP_ANYLETTER:
  3364. begin
  3365. if (reginput = fInputEnd) or not IsWordChar(reginput^) // ###0.943
  3366. then
  3367. Exit;
  3368. Inc(reginput);
  3369. end;
  3370. OP_NOTLETTER:
  3371. begin
  3372. if (reginput = fInputEnd) or IsWordChar(reginput^) // ###0.943
  3373. then
  3374. Exit;
  3375. Inc(reginput);
  3376. end;
  3377. OP_ANYSPACE:
  3378. begin
  3379. if (reginput = fInputEnd) or not IsSpaceChar(reginput^) // ###0.943
  3380. then
  3381. Exit;
  3382. Inc(reginput);
  3383. end;
  3384. OP_NOTSPACE:
  3385. begin
  3386. if (reginput = fInputEnd) or IsSpaceChar(reginput^) // ###0.943
  3387. then
  3388. Exit;
  3389. Inc(reginput);
  3390. end;
  3391. OP_ANYVERTSEP:
  3392. begin
  3393. if (reginput = fInputEnd) or not IsLineSeparator(reginput^) then
  3394. Exit;
  3395. Inc(reginput);
  3396. end;
  3397. OP_NOTVERTSEP:
  3398. begin
  3399. if (reginput = fInputEnd) or IsLineSeparator(reginput^) then
  3400. Exit;
  3401. Inc(reginput);
  3402. end;
  3403. OP_ANYHORZSEP:
  3404. begin
  3405. if (reginput = fInputEnd) or not IsHorzSeparator(reginput^) then
  3406. Exit;
  3407. Inc(reginput);
  3408. end;
  3409. OP_NOTHORZSEP:
  3410. begin
  3411. if (reginput = fInputEnd) or IsHorzSeparator(reginput^) then
  3412. Exit;
  3413. Inc(reginput);
  3414. end;
  3415. OP_EXACTLYCI:
  3416. begin
  3417. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  3418. Len := PLongInt(opnd)^;
  3419. Inc(opnd, RENumberSz);
  3420. // Inline the first character, for speed.
  3421. if (opnd^ <> reginput^) and (InvertCase(opnd^) <> reginput^) then
  3422. Exit;
  3423. // ###0.929 begin
  3424. no := Len;
  3425. save := reginput;
  3426. while no > 1 do
  3427. begin
  3428. Inc(save);
  3429. Inc(opnd);
  3430. if (opnd^ <> save^) and (InvertCase(opnd^) <> save^) then
  3431. Exit;
  3432. Dec(no);
  3433. end;
  3434. // ###0.929 end
  3435. Inc(reginput, Len);
  3436. end;
  3437. OP_EXACTLY:
  3438. begin
  3439. opnd := scan + REOpSz + RENextOffSz; // OPERAND
  3440. Len := PLongInt(opnd)^;
  3441. Inc(opnd, RENumberSz);
  3442. // Inline the first character, for speed.
  3443. if opnd^ <> reginput^ then
  3444. Exit;
  3445. // ###0.929 begin
  3446. no := Len;
  3447. save := reginput;
  3448. while no > 1 do
  3449. begin
  3450. Inc(save);
  3451. Inc(opnd);
  3452. if opnd^ <> save^ then
  3453. Exit;
  3454. Dec(no);
  3455. end;
  3456. // ###0.929 end
  3457. Inc(reginput, Len);
  3458. end;
  3459. OP_BSUBEXP:
  3460. begin // ###0.936
  3461. no := Ord((scan + REOpSz + RENextOffSz)^);
  3462. no := GrpIndexes[no];
  3463. if no < 0 then
  3464. Exit;
  3465. if startp[no] = nil then
  3466. Exit;
  3467. if endp[no] = nil then
  3468. Exit;
  3469. save := reginput;
  3470. opnd := startp[no];
  3471. while opnd < endp[no] do
  3472. begin
  3473. if (save >= fInputEnd) or (save^ <> opnd^) then
  3474. Exit;
  3475. Inc(save);
  3476. Inc(opnd);
  3477. end;
  3478. reginput := save;
  3479. end;
  3480. OP_BSUBEXPCI:
  3481. begin // ###0.936
  3482. no := Ord((scan + REOpSz + RENextOffSz)^);
  3483. no := GrpIndexes[no];
  3484. if no < 0 then
  3485. Exit;
  3486. if startp[no] = nil then
  3487. Exit;
  3488. if endp[no] = nil then
  3489. Exit;
  3490. save := reginput;
  3491. opnd := startp[no];
  3492. while opnd < endp[no] do
  3493. begin
  3494. if (save >= fInputEnd) or
  3495. ((save^ <> opnd^) and (save^ <> InvertCase(opnd^))) then
  3496. Exit;
  3497. Inc(save);
  3498. Inc(opnd);
  3499. end;
  3500. reginput := save;
  3501. end;
  3502. OP_ANYOF:
  3503. begin
  3504. if (reginput = fInputEnd) or
  3505. not FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, False) then
  3506. Exit;
  3507. Inc(reginput);
  3508. end;
  3509. OP_ANYBUT:
  3510. begin
  3511. if (reginput = fInputEnd) or
  3512. FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, False) then
  3513. Exit;
  3514. Inc(reginput);
  3515. end;
  3516. OP_ANYOFCI:
  3517. begin
  3518. if (reginput = fInputEnd) or
  3519. not FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, True) then
  3520. Exit;
  3521. Inc(reginput);
  3522. end;
  3523. OP_ANYBUTCI:
  3524. begin
  3525. if (reginput = fInputEnd) or
  3526. FindInCharClass(scan + REOpSz + RENextOffSz, reginput^, True) then
  3527. Exit;
  3528. Inc(reginput);
  3529. end;
  3530. OP_NOTHING:
  3531. ;
  3532. OP_COMMENT:
  3533. ;
  3534. OP_BACK:
  3535. ;
  3536. Succ(OP_OPEN) .. TREOp(Ord(OP_OPEN) + NSUBEXP - 1):
  3537. begin // ###0.929
  3538. no := Ord(scan^) - Ord(OP_OPEN);
  3539. // save := reginput;
  3540. save := startp[no]; // ###0.936
  3541. startp[no] := reginput; // ###0.936
  3542. Result := MatchPrim(next);
  3543. if not Result // ###0.936
  3544. then
  3545. startp[no] := save;
  3546. // if Result and (startp [no] = nil)
  3547. // then startp [no] := save;
  3548. // Don't set startp if some later invocation of the same
  3549. // parentheses already has.
  3550. Exit;
  3551. end;
  3552. Succ(OP_CLOSE) .. TREOp(Ord(OP_CLOSE) + NSUBEXP - 1):
  3553. begin // ###0.929
  3554. no := Ord(scan^) - Ord(OP_CLOSE);
  3555. // save := reginput;
  3556. save := endp[no]; // ###0.936
  3557. endp[no] := reginput; // ###0.936
  3558. Result := MatchPrim(next);
  3559. if not Result // ###0.936
  3560. then
  3561. endp[no] := save;
  3562. // if Result and (endp [no] = nil)
  3563. // then endp [no] := save;
  3564. // Don't set endp if some later invocation of the same
  3565. // parentheses already has.
  3566. Exit;
  3567. end;
  3568. OP_BRANCH:
  3569. begin
  3570. if (next^ <> OP_BRANCH) // No choice.
  3571. then
  3572. next := scan + REOpSz + RENextOffSz // Avoid recursion
  3573. else
  3574. begin
  3575. repeat
  3576. save := reginput;
  3577. Result := MatchPrim(scan + REOpSz + RENextOffSz);
  3578. if Result then
  3579. Exit;
  3580. reginput := save;
  3581. scan := regnext(scan);
  3582. until (scan = nil) or (scan^ <> OP_BRANCH);
  3583. Exit;
  3584. end;
  3585. end;
  3586. {$IFDEF ComplexBraces}
  3587. OP_LOOPENTRY:
  3588. begin // ###0.925
  3589. no := LoopStackIdx;
  3590. Inc(LoopStackIdx);
  3591. if LoopStackIdx > LoopStackMax then
  3592. begin
  3593. Error(reeLoopStackExceeded);
  3594. Exit;
  3595. end;
  3596. save := reginput;
  3597. LoopStack[LoopStackIdx] := 0; // init loop counter
  3598. Result := MatchPrim(next); // execute loop
  3599. LoopStackIdx := no; // cleanup
  3600. if Result then
  3601. Exit;
  3602. reginput := save;
  3603. Exit;
  3604. end;
  3605. OP_LOOP, OP_LOOPNG:
  3606. begin // ###0.940
  3607. if LoopStackIdx <= 0 then
  3608. begin
  3609. Error(reeLoopWithoutEntry);
  3610. Exit;
  3611. end;
  3612. opnd := scan + PRENextOff(AlignToPtr(scan + REOpSz + RENextOffSz + 2 * REBracesArgSz))^;
  3613. BracesMin := PREBracesArg(AlignToInt(scan + REOpSz + RENextOffSz))^;
  3614. Bracesmax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  3615. save := reginput;
  3616. if LoopStack[LoopStackIdx] >= BracesMin then
  3617. begin // Min alredy matched - we can work
  3618. if scan^ = OP_LOOP then
  3619. begin
  3620. // greedy way - first try to max deep of greed ;)
  3621. if LoopStack[LoopStackIdx] < Bracesmax then
  3622. begin
  3623. Inc(LoopStack[LoopStackIdx]);
  3624. no := LoopStackIdx;
  3625. Result := MatchPrim(opnd);
  3626. LoopStackIdx := no;
  3627. if Result then
  3628. Exit;
  3629. reginput := save;
  3630. end;
  3631. Dec(LoopStackIdx); // Fail. May be we are too greedy? ;)
  3632. Result := MatchPrim(next);
  3633. if not Result then
  3634. reginput := save;
  3635. Exit;
  3636. end
  3637. else
  3638. begin
  3639. // non-greedy - try just now
  3640. Result := MatchPrim(next);
  3641. if Result then
  3642. Exit
  3643. else
  3644. reginput := save; // failed - move next and try again
  3645. if LoopStack[LoopStackIdx] < Bracesmax then
  3646. begin
  3647. Inc(LoopStack[LoopStackIdx]);
  3648. no := LoopStackIdx;
  3649. Result := MatchPrim(opnd);
  3650. LoopStackIdx := no;
  3651. if Result then
  3652. Exit;
  3653. reginput := save;
  3654. end;
  3655. Dec(LoopStackIdx); // Failed - back up
  3656. Exit;
  3657. end
  3658. end
  3659. else
  3660. begin // first match a min_cnt times
  3661. Inc(LoopStack[LoopStackIdx]);
  3662. no := LoopStackIdx;
  3663. Result := MatchPrim(opnd);
  3664. LoopStackIdx := no;
  3665. if Result then
  3666. Exit;
  3667. Dec(LoopStack[LoopStackIdx]);
  3668. reginput := save;
  3669. Exit;
  3670. end;
  3671. end;
  3672. {$ENDIF}
  3673. OP_STAR, OP_PLUS, OP_BRACES, OP_STARNG, OP_PLUSNG, OP_BRACESNG:
  3674. begin
  3675. // Lookahead to avoid useless match attempts when we know
  3676. // what character comes next.
  3677. nextch := #0;
  3678. if next^ = OP_EXACTLY then
  3679. nextch := (next + REOpSz + RENextOffSz + RENumberSz)^;
  3680. Bracesmax := MaxInt; // infinite loop for * and + //###0.92
  3681. if (scan^ = OP_STAR) or (scan^ = OP_STARNG) then
  3682. BracesMin := 0 // star
  3683. else if (scan^ = OP_PLUS) or (scan^ = OP_PLUSNG) then
  3684. BracesMin := 1 // plus
  3685. else
  3686. begin // braces
  3687. BracesMin := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  3688. Bracesmax := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz))^;
  3689. end;
  3690. save := reginput;
  3691. opnd := scan + REOpSz + RENextOffSz;
  3692. if (scan^ = OP_BRACES) or (scan^ = OP_BRACESNG) then
  3693. Inc(opnd, 2 * REBracesArgSz);
  3694. if (scan^ = OP_PLUSNG) or (scan^ = OP_STARNG) or (scan^ = OP_BRACESNG) then
  3695. begin
  3696. // non-greedy mode
  3697. Bracesmax := regrepeat(opnd, Bracesmax);
  3698. // don't repeat more than BracesMax
  3699. // Now we know real Max limit to move forward (for recursion 'back up')
  3700. // In some cases it can be faster to check only Min positions first,
  3701. // but after that we have to check every position separtely instead
  3702. // of fast scannig in loop.
  3703. no := BracesMin;
  3704. while no <= Bracesmax do
  3705. begin
  3706. reginput := save + no;
  3707. // If it could work, try it.
  3708. if (nextch = #0) or (reginput^ = nextch) then
  3709. begin
  3710. {$IFDEF ComplexBraces}
  3711. System.Move(LoopStack, SavedLoopStack, SizeOf(LoopStack));
  3712. // ###0.925
  3713. SavedLoopStackIdx := LoopStackIdx;
  3714. {$ENDIF}
  3715. if MatchPrim(next) then
  3716. begin
  3717. Result := True;
  3718. Exit;
  3719. end;
  3720. {$IFDEF ComplexBraces}
  3721. System.Move(SavedLoopStack, LoopStack, SizeOf(LoopStack));
  3722. LoopStackIdx := SavedLoopStackIdx;
  3723. {$ENDIF}
  3724. end;
  3725. Inc(no); // Couldn't or didn't - move forward.
  3726. end; { of while }
  3727. Exit;
  3728. end
  3729. else
  3730. begin // greedy mode
  3731. no := regrepeat(opnd, Bracesmax); // don't repeat more than max_cnt
  3732. while no >= BracesMin do
  3733. begin
  3734. // If it could work, try it.
  3735. if (nextch = #0) or (reginput^ = nextch) then
  3736. begin
  3737. {$IFDEF ComplexBraces}
  3738. System.Move(LoopStack, SavedLoopStack, SizeOf(LoopStack));
  3739. // ###0.925
  3740. SavedLoopStackIdx := LoopStackIdx;
  3741. {$ENDIF}
  3742. if MatchPrim(next) then
  3743. begin
  3744. Result := True;
  3745. Exit;
  3746. end;
  3747. {$IFDEF ComplexBraces}
  3748. System.Move(SavedLoopStack, LoopStack, SizeOf(LoopStack));
  3749. LoopStackIdx := SavedLoopStackIdx;
  3750. {$ENDIF}
  3751. end;
  3752. Dec(no); // Couldn't or didn't - back up.
  3753. reginput := save + no;
  3754. end; { of while }
  3755. Exit;
  3756. end;
  3757. end;
  3758. OP_EEND:
  3759. begin
  3760. Result := True; // Success!
  3761. Exit;
  3762. end;
  3763. else
  3764. begin
  3765. Error(reeMatchPrimMemoryCorruption);
  3766. Exit;
  3767. end;
  3768. end; { of case scan^ }
  3769. scan := next;
  3770. end; { of while scan <> nil }
  3771. // We get here only if there's trouble -- normally "case EEND" is the
  3772. // terminating point.
  3773. Error(reeMatchPrimCorruptedPointers);
  3774. end; { of function TRegExpr.MatchPrim
  3775. -------------------------------------------------------------- }
  3776. function TRegExpr.Exec(const AInputString: RegExprString): boolean;
  3777. begin
  3778. InputString := AInputString;
  3779. Result := ExecPrim(1, False, False);
  3780. end; { of function TRegExpr.Exec
  3781. -------------------------------------------------------------- }
  3782. function TRegExpr.Exec: boolean;
  3783. var
  3784. SlowChecks: boolean;
  3785. begin
  3786. SlowChecks := Length(fInputString) < fSlowChecksSizeMax;
  3787. Result := ExecPrim(1, False, SlowChecks);
  3788. end; { of function TRegExpr.Exec
  3789. -------------------------------------------------------------- }
  3790. function TRegExpr.Exec(AOffset: integer): boolean;
  3791. begin
  3792. Result := ExecPrim(AOffset, False, False);
  3793. end; { of function TRegExpr.Exec
  3794. -------------------------------------------------------------- }
  3795. function TRegExpr.ExecPos(AOffset: integer = 1): boolean;
  3796. begin
  3797. Result := ExecPrim(AOffset, False, False);
  3798. end; { of function TRegExpr.ExecPos
  3799. -------------------------------------------------------------- }
  3800. function TRegExpr.ExecPos(AOffset: integer; ATryOnce: boolean): boolean;
  3801. begin
  3802. Result := ExecPrim(AOffset, ATryOnce, False);
  3803. end;
  3804. function TRegExpr.MatchAtOnePos(APos: PRegExprChar): boolean;
  3805. begin
  3806. reginput := APos;
  3807. Result := MatchPrim(programm + REOpSz);
  3808. if Result then
  3809. begin
  3810. startp[0] := APos;
  3811. endp[0] := reginput;
  3812. end;
  3813. end;
  3814. procedure TRegExpr.ClearMatches;
  3815. begin
  3816. FillChar(startp, SizeOf(startp), 0);
  3817. FillChar(endp, SizeOf(endp), 0);
  3818. end;
  3819. procedure TRegExpr.ClearInternalIndexes;
  3820. var
  3821. i: integer;
  3822. begin
  3823. FillChar(startp, SizeOf(startp), 0);
  3824. FillChar(endp, SizeOf(endp), 0);
  3825. for i := 0 to NSUBEXP - 1 do
  3826. GrpIndexes[i] := -1;
  3827. GrpIndexes[0] := 0;
  3828. GrpCount := 0;
  3829. end;
  3830. function TRegExpr.ExecPrim(AOffset: integer; ATryOnce, ASlowChecks: boolean): boolean;
  3831. var
  3832. Ptr: PRegExprChar;
  3833. begin
  3834. Result := False;
  3835. // Ensure that Match cleared either if optimization tricks or some error
  3836. // will lead to leaving ExecPrim without actual search. That is
  3837. // important for ExecNext logic and so on.
  3838. ClearMatches;
  3839. // Don't check IsProgrammOk here! it causes big slowdown in test_benchmark!
  3840. if programm = nil then
  3841. begin
  3842. Compile;
  3843. if programm = nil then
  3844. Exit;
  3845. end;
  3846. // Check InputString presence
  3847. if fInputString = '' then
  3848. begin
  3849. Error(reeNoInputStringSpecified);
  3850. Exit;
  3851. end;
  3852. // Check that the start position is not negative
  3853. if AOffset < 1 then
  3854. begin
  3855. Error(reeOffsetMustBePositive);
  3856. Exit;
  3857. end;
  3858. // Check that the start position is not longer than the line
  3859. // If so then exit with nothing found
  3860. if AOffset > (Length(fInputString) + 1) // for matching empty string after last char.
  3861. then
  3862. Exit;
  3863. Ptr := fInputStart + AOffset - 1;
  3864. // If there is a "must appear" string, look for it.
  3865. if ASlowChecks then
  3866. if regmustString <> '' then
  3867. if Pos(regmustString, fInputString) = 0 then Exit;
  3868. {$IFDEF ComplexBraces}
  3869. // no loops started
  3870. LoopStackIdx := 0; // ###0.925
  3871. {$ENDIF}
  3872. // ATryOnce or anchored match (it needs to be tried only once).
  3873. if ATryOnce or (reganchored <> #0) then
  3874. begin
  3875. {$IFDEF UseFirstCharSet}
  3876. {$IFDEF UniCode}
  3877. if Ord(Ptr^) <= $FF then
  3878. {$ENDIF}
  3879. if not FirstCharArray[byte(Ptr^)] then
  3880. Exit;
  3881. {$ENDIF}
  3882. Result := MatchAtOnePos(Ptr);
  3883. Exit;
  3884. end;
  3885. // Messy cases: unanchored match.
  3886. Dec(Ptr);
  3887. repeat
  3888. Inc(Ptr);
  3889. if Ptr > fInputEnd then
  3890. Exit;
  3891. {$IFDEF UseFirstCharSet}
  3892. {$IFDEF UniCode}
  3893. if Ord(Ptr^) <= $FF then
  3894. {$ENDIF}
  3895. if not FirstCharArray[byte(Ptr^)] then
  3896. Continue;
  3897. {$ENDIF}
  3898. Result := MatchAtOnePos(Ptr);
  3899. // Exit on a match or after testing the end-of-string
  3900. if Result then
  3901. Exit;
  3902. until False;
  3903. end; { of function TRegExpr.ExecPrim
  3904. -------------------------------------------------------------- }
  3905. function TRegExpr.ExecNext: boolean;
  3906. var
  3907. PtrBegin, PtrEnd: PRegExprChar;
  3908. Offset: PtrInt;
  3909. begin
  3910. PtrBegin := startp[0];
  3911. PtrEnd := endp[0];
  3912. if (PtrBegin = nil) or (PtrEnd = nil) then
  3913. begin
  3914. Error(reeExecNextWithoutExec);
  3915. Result := False;
  3916. Exit;
  3917. end;
  3918. Offset := PtrEnd - fInputStart + 1;
  3919. // prevent infinite looping if empty string matches r.e.
  3920. if PtrBegin = PtrEnd then
  3921. Inc(Offset);
  3922. Result := ExecPrim(Offset, False, False);
  3923. end; { of function TRegExpr.ExecNext
  3924. -------------------------------------------------------------- }
  3925. procedure TRegExpr.SetInputString(const AInputString: RegExprString);
  3926. begin
  3927. ClearMatches;
  3928. fInputString := AInputString;
  3929. UniqueString(fInputString);
  3930. fInputStart := PRegExprChar(fInputString);
  3931. fInputEnd := fInputStart + Length(fInputString);
  3932. end; { of procedure TRegExpr.SetInputString
  3933. -------------------------------------------------------------- }
  3934. procedure TRegExpr.SetLineSeparators(const AStr: RegExprString);
  3935. begin
  3936. if AStr <> fLineSeparators then
  3937. begin
  3938. fLineSeparators := AStr;
  3939. InitLineSepArray;
  3940. InvalidateProgramm;
  3941. end;
  3942. end; { of procedure TRegExpr.SetLineSeparators
  3943. -------------------------------------------------------------- }
  3944. procedure TRegExpr.SetLinePairedSeparator(const AStr: RegExprString);
  3945. begin
  3946. if Length(AStr) = 2 then
  3947. begin
  3948. if AStr[1] = AStr[2] then
  3949. begin
  3950. // it's impossible for our 'one-point' checking to support
  3951. // two chars separator for identical chars
  3952. Error(reeBadLinePairedSeparator);
  3953. Exit;
  3954. end;
  3955. if not fLinePairedSeparatorAssigned or (AStr[1] <> fLinePairedSeparatorHead)
  3956. or (AStr[2] <> fLinePairedSeparatorTail) then
  3957. begin
  3958. fLinePairedSeparatorAssigned := True;
  3959. fLinePairedSeparatorHead := AStr[1];
  3960. fLinePairedSeparatorTail := AStr[2];
  3961. InvalidateProgramm;
  3962. end;
  3963. end
  3964. else if Length(AStr) = 0 then
  3965. begin
  3966. if fLinePairedSeparatorAssigned then
  3967. begin
  3968. fLinePairedSeparatorAssigned := False;
  3969. InvalidateProgramm;
  3970. end;
  3971. end
  3972. else
  3973. Error(reeBadLinePairedSeparator);
  3974. end; { of procedure TRegExpr.SetLinePairedSeparator
  3975. -------------------------------------------------------------- }
  3976. function TRegExpr.GetLinePairedSeparator: RegExprString;
  3977. begin
  3978. if fLinePairedSeparatorAssigned then
  3979. begin
  3980. {$IFDEF UniCode}
  3981. // Here is some UniCode 'magic'
  3982. // If You do know better decision to concatenate
  3983. // two WideChars, please, let me know!
  3984. Result := fLinePairedSeparatorHead; // ###0.947
  3985. Result := Result + fLinePairedSeparatorTail;
  3986. {$ELSE}
  3987. Result := fLinePairedSeparatorHead + fLinePairedSeparatorTail;
  3988. {$ENDIF}
  3989. end
  3990. else
  3991. Result := '';
  3992. end; { of function TRegExpr.GetLinePairedSeparator
  3993. -------------------------------------------------------------- }
  3994. function TRegExpr.Substitute(const ATemplate: RegExprString): RegExprString;
  3995. // perform substitutions after a regexp match
  3996. var
  3997. TemplateBeg, TemplateEnd: PRegExprChar;
  3998. function ParseVarName(var APtr: PRegExprChar): integer;
  3999. // extract name of variable (digits, may be enclosed with
  4000. // curly braces) from APtr^, uses TemplateEnd !!!
  4001. var
  4002. p: PRegExprChar;
  4003. Delimited: boolean;
  4004. begin
  4005. Result := 0;
  4006. p := APtr;
  4007. Delimited := (p < TemplateEnd) and (p^ = '{');
  4008. if Delimited then
  4009. Inc(p); // skip left curly brace
  4010. if (p < TemplateEnd) and (p^ = '&') then
  4011. Inc(p) // this is '$&' or '${&}'
  4012. else
  4013. while (p < TemplateEnd) and IsDigitChar(p^) do
  4014. begin
  4015. Result := Result * 10 + (Ord(p^) - Ord('0')); // ###0.939
  4016. Inc(p);
  4017. end;
  4018. if Delimited then
  4019. if (p < TemplateEnd) and (p^ = '}') then
  4020. Inc(p) // skip right curly brace
  4021. else
  4022. p := APtr; // isn't properly terminated
  4023. if p = APtr then
  4024. Result := -1; // no valid digits found or no right curly brace
  4025. APtr := p;
  4026. end;
  4027. type
  4028. TSubstMode = (smodeNormal, smodeOneUpper, smodeOneLower, smodeAllUpper, smodeAllLower);
  4029. var
  4030. Mode: TSubstMode;
  4031. p, p0, p1, ResultPtr: PRegExprChar;
  4032. ResultLen, n: integer;
  4033. Ch, QuotedChar: REChar;
  4034. begin
  4035. // Check programm and input string
  4036. if not IsProgrammOk then
  4037. Exit;
  4038. if fInputString = '' then
  4039. begin
  4040. Error(reeNoInputStringSpecified);
  4041. Exit;
  4042. end;
  4043. // Prepare for working
  4044. if ATemplate = '' then
  4045. begin // prevent nil pointers
  4046. Result := '';
  4047. Exit;
  4048. end;
  4049. TemplateBeg := PRegExprChar(ATemplate);
  4050. TemplateEnd := TemplateBeg + Length(ATemplate);
  4051. // Count result length for speed optimization.
  4052. ResultLen := 0;
  4053. p := TemplateBeg;
  4054. while p < TemplateEnd do
  4055. begin
  4056. Ch := p^;
  4057. Inc(p);
  4058. if Ch = '$' then
  4059. n := GrpIndexes[ParseVarName(p)]
  4060. else
  4061. n := -1;
  4062. if n >= 0 then
  4063. begin
  4064. Inc(ResultLen, endp[n] - startp[n]);
  4065. end
  4066. else
  4067. begin
  4068. if (Ch = EscChar) and (p < TemplateEnd) then
  4069. begin // quoted or special char followed
  4070. Ch := p^;
  4071. Inc(p);
  4072. case Ch of
  4073. 'n':
  4074. Inc(ResultLen, Length(FReplaceLineEnd));
  4075. 'u', 'l', 'U', 'L': { nothing }
  4076. ;
  4077. 'x':
  4078. begin
  4079. Inc(ResultLen);
  4080. if (p^ = '{') then
  4081. begin // skip \x{....}
  4082. while ((p^ <> '}') and (p < TemplateEnd)) do
  4083. p := p + 1;
  4084. p := p + 1;
  4085. end
  4086. else
  4087. p := p + 2 // skip \x..
  4088. end;
  4089. else
  4090. Inc(ResultLen);
  4091. end;
  4092. end
  4093. else
  4094. Inc(ResultLen);
  4095. end;
  4096. end;
  4097. // Get memory. We do it once and it significant speed up work !
  4098. if ResultLen = 0 then
  4099. begin
  4100. Result := '';
  4101. Exit;
  4102. end;
  4103. SetLength(Result, ResultLen);
  4104. // Fill Result
  4105. ResultPtr := Pointer(Result);
  4106. p := TemplateBeg;
  4107. Mode := smodeNormal;
  4108. while p < TemplateEnd do
  4109. begin
  4110. Ch := p^;
  4111. p0 := p;
  4112. Inc(p);
  4113. p1 := p;
  4114. if Ch = '$' then
  4115. n := GrpIndexes[ParseVarName(p)]
  4116. else
  4117. n := -1;
  4118. if (n >= 0) then
  4119. begin
  4120. p0 := startp[n];
  4121. p1 := endp[n];
  4122. end
  4123. else
  4124. begin
  4125. if (Ch = EscChar) and (p < TemplateEnd) then
  4126. begin // quoted or special char followed
  4127. Ch := p^;
  4128. Inc(p);
  4129. case Ch of
  4130. 'n':
  4131. begin
  4132. p0 := PRegExprChar(FReplaceLineEnd);
  4133. p1 := p0 + Length(FReplaceLineEnd);
  4134. end;
  4135. 'x', 't', 'r', 'f', 'a', 'e':
  4136. begin
  4137. p := p - 1;
  4138. // UnquoteChar expects the escaped char under the pointer
  4139. QuotedChar := UnQuoteChar(p);
  4140. p := p + 1;
  4141. // Skip after last part of the escaped sequence - UnquoteChar stops on the last symbol of it
  4142. p0 := @QuotedChar;
  4143. p1 := p0 + 1;
  4144. end;
  4145. 'l':
  4146. begin
  4147. Mode := smodeOneLower;
  4148. p1 := p0;
  4149. end;
  4150. 'L':
  4151. begin
  4152. Mode := smodeAllLower;
  4153. p1 := p0;
  4154. end;
  4155. 'u':
  4156. begin
  4157. Mode := smodeOneUpper;
  4158. p1 := p0;
  4159. end;
  4160. 'U':
  4161. begin
  4162. Mode := smodeAllUpper;
  4163. p1 := p0;
  4164. end;
  4165. else
  4166. begin
  4167. Inc(p0);
  4168. Inc(p1);
  4169. end;
  4170. end;
  4171. end
  4172. end;
  4173. if p0 < p1 then
  4174. begin
  4175. while p0 < p1 do
  4176. begin
  4177. case Mode of
  4178. smodeOneLower:
  4179. begin
  4180. ResultPtr^ := _LowerCase(p0^);
  4181. Mode := smodeNormal;
  4182. end;
  4183. smodeAllLower:
  4184. begin
  4185. ResultPtr^ := _LowerCase(p0^);
  4186. end;
  4187. smodeOneUpper:
  4188. begin
  4189. ResultPtr^ := _UpperCase(p0^);
  4190. Mode := smodeNormal;
  4191. end;
  4192. smodeAllUpper:
  4193. begin
  4194. ResultPtr^ := _UpperCase(p0^);
  4195. end;
  4196. else
  4197. ResultPtr^ := p0^;
  4198. end;
  4199. Inc(ResultPtr);
  4200. Inc(p0);
  4201. end;
  4202. Mode := smodeNormal;
  4203. end;
  4204. end;
  4205. end; { of function TRegExpr.Substitute
  4206. -------------------------------------------------------------- }
  4207. procedure TRegExpr.Split(const AInputStr: RegExprString; APieces: TStrings);
  4208. var
  4209. PrevPos: PtrInt;
  4210. begin
  4211. PrevPos := 1;
  4212. if Exec(AInputStr) then
  4213. repeat
  4214. APieces.Add(System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos));
  4215. PrevPos := MatchPos[0] + MatchLen[0];
  4216. until not ExecNext;
  4217. APieces.Add(System.Copy(AInputStr, PrevPos, MaxInt)); // Tail
  4218. end; { of procedure TRegExpr.Split
  4219. -------------------------------------------------------------- }
  4220. function TRegExpr.Replace(const AInputStr: RegExprString;
  4221. const AReplaceStr: RegExprString;
  4222. AUseSubstitution: boolean = False): RegExprString;
  4223. var
  4224. PrevPos: PtrInt;
  4225. begin
  4226. Result := '';
  4227. PrevPos := 1;
  4228. if Exec(AInputStr) then
  4229. repeat
  4230. Result := Result + System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos);
  4231. if AUseSubstitution // ###0.946
  4232. then
  4233. Result := Result + Substitute(AReplaceStr)
  4234. else
  4235. Result := Result + AReplaceStr;
  4236. PrevPos := MatchPos[0] + MatchLen[0];
  4237. until not ExecNext;
  4238. Result := Result + System.Copy(AInputStr, PrevPos, MaxInt); // Tail
  4239. end; { of function TRegExpr.Replace
  4240. -------------------------------------------------------------- }
  4241. function TRegExpr.ReplaceEx(const AInputStr: RegExprString;
  4242. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  4243. var
  4244. PrevPos: PtrInt;
  4245. begin
  4246. Result := '';
  4247. PrevPos := 1;
  4248. if Exec(AInputStr) then
  4249. repeat
  4250. Result := Result + System.Copy(AInputStr, PrevPos, MatchPos[0] - PrevPos)
  4251. + AReplaceFunc(Self);
  4252. PrevPos := MatchPos[0] + MatchLen[0];
  4253. until not ExecNext;
  4254. Result := Result + System.Copy(AInputStr, PrevPos, MaxInt); // Tail
  4255. end; { of function TRegExpr.ReplaceEx
  4256. -------------------------------------------------------------- }
  4257. function TRegExpr.Replace(const AInputStr: RegExprString;
  4258. AReplaceFunc: TRegExprReplaceFunction): RegExprString;
  4259. begin
  4260. Result := ReplaceEx(AInputStr, AReplaceFunc);
  4261. end; { of function TRegExpr.Replace
  4262. -------------------------------------------------------------- }
  4263. { ============================================================= }
  4264. { ====================== Debug section ======================== }
  4265. { ============================================================= }
  4266. {$IFDEF UseFirstCharSet}
  4267. procedure TRegExpr.FillFirstCharSet(prog: PRegExprChar);
  4268. var
  4269. scan: PRegExprChar; // Current node.
  4270. Next: PRegExprChar; // Next node.
  4271. opnd: PRegExprChar;
  4272. Oper: TREOp;
  4273. ch: REChar;
  4274. min_cnt, i: integer;
  4275. TempSet: TRegExprCharset;
  4276. begin
  4277. TempSet := [];
  4278. scan := prog;
  4279. while scan <> nil do
  4280. begin
  4281. Next := regnext(scan);
  4282. Oper := PREOp(scan)^;
  4283. case Oper of
  4284. OP_BSUBEXP,
  4285. OP_BSUBEXPCI:
  4286. begin
  4287. // we cannot optimize r.e. if it starts with back reference
  4288. FirstCharSet := RegExprAllSet; //###0.930
  4289. Exit;
  4290. end;
  4291. OP_BOL,
  4292. OP_BOLML:
  4293. ; // Exit; //###0.937
  4294. OP_EOL,
  4295. OP_EOLML:
  4296. begin //###0.948 was empty in 0.947, was EXIT in 0.937
  4297. Include(FirstCharSet, 0);
  4298. if ModifierM then
  4299. for i := 1 to Length(LineSeparators) do
  4300. Include(FirstCharSet, byte(LineSeparators[i]));
  4301. Exit;
  4302. end;
  4303. OP_BOUND,
  4304. OP_NOTBOUND:
  4305. ; //###0.943 ?!!
  4306. OP_ANY,
  4307. OP_ANYML:
  4308. begin // we can better define ANYML !!!
  4309. FirstCharSet := RegExprAllSet; //###0.930
  4310. Exit;
  4311. end;
  4312. OP_ANYDIGIT:
  4313. begin
  4314. FirstCharSet := FirstCharSet + RegExprDigitSet;
  4315. Exit;
  4316. end;
  4317. OP_NOTDIGIT:
  4318. begin
  4319. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprDigitSet);
  4320. Exit;
  4321. end;
  4322. OP_ANYLETTER:
  4323. begin
  4324. GetCharSetFromWordChars(TempSet);
  4325. FirstCharSet := FirstCharSet + TempSet;
  4326. Exit;
  4327. end;
  4328. OP_NOTLETTER:
  4329. begin
  4330. GetCharSetFromWordChars(TempSet);
  4331. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4332. Exit;
  4333. end;
  4334. OP_ANYSPACE:
  4335. begin
  4336. GetCharSetFromSpaceChars(TempSet);
  4337. FirstCharSet := FirstCharSet + TempSet;
  4338. Exit;
  4339. end;
  4340. OP_NOTSPACE:
  4341. begin
  4342. GetCharSetFromSpaceChars(TempSet);
  4343. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4344. Exit;
  4345. end;
  4346. OP_ANYVERTSEP:
  4347. begin
  4348. FirstCharSet := FirstCharSet + RegExprLineSeparatorsSet;
  4349. Exit;
  4350. end;
  4351. OP_NOTVERTSEP:
  4352. begin
  4353. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprLineSeparatorsSet);
  4354. Exit;
  4355. end;
  4356. OP_ANYHORZSEP:
  4357. begin
  4358. FirstCharSet := FirstCharSet + RegExprHorzSeparatorsSet;
  4359. Exit;
  4360. end;
  4361. OP_NOTHORZSEP:
  4362. begin
  4363. FirstCharSet := FirstCharSet + (RegExprAllSet - RegExprHorzSeparatorsSet);
  4364. Exit;
  4365. end;
  4366. OP_EXACTLYCI:
  4367. begin
  4368. ch := (scan + REOpSz + RENextOffSz + RENumberSz)^;
  4369. {$IFDEF UniCode}
  4370. if Ord(ch) <= $FF then
  4371. {$ENDIF}
  4372. begin
  4373. Include(FirstCharSet, byte(ch));
  4374. Include(FirstCharSet, byte(InvertCase(ch)));
  4375. end;
  4376. Exit;
  4377. end;
  4378. OP_EXACTLY:
  4379. begin
  4380. ch := (scan + REOpSz + RENextOffSz + RENumberSz)^;
  4381. {$IFDEF UniCode}
  4382. if Ord(ch) <= $FF then
  4383. {$ENDIF}
  4384. Include(FirstCharSet, byte(ch));
  4385. Exit;
  4386. end;
  4387. OP_ANYOF:
  4388. begin
  4389. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, False, TempSet);
  4390. FirstCharSet := FirstCharSet + TempSet;
  4391. Exit;
  4392. end;
  4393. OP_ANYBUT:
  4394. begin
  4395. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, False, TempSet);
  4396. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4397. Exit;
  4398. end;
  4399. OP_ANYOFCI:
  4400. begin
  4401. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, True, TempSet);
  4402. FirstCharSet := FirstCharSet + TempSet;
  4403. Exit;
  4404. end;
  4405. OP_ANYBUTCI:
  4406. begin
  4407. GetCharSetFromCharClass(scan + REOpSz + RENextOffSz, True, TempSet);
  4408. FirstCharSet := FirstCharSet + (RegExprAllSet - TempSet);
  4409. Exit;
  4410. end;
  4411. OP_NOTHING:
  4412. ;
  4413. OP_COMMENT:
  4414. ;
  4415. OP_BACK:
  4416. ;
  4417. Succ(OP_OPEN) .. TREOp(Ord(OP_OPEN) + NSUBEXP - 1):
  4418. begin //###0.929
  4419. FillFirstCharSet(Next);
  4420. Exit;
  4421. end;
  4422. Succ(OP_CLOSE) .. TREOp(Ord(OP_CLOSE) + NSUBEXP - 1):
  4423. begin //###0.929
  4424. FillFirstCharSet(Next);
  4425. Exit;
  4426. end;
  4427. OP_BRANCH:
  4428. begin
  4429. if (PREOp(Next)^ <> OP_BRANCH) // No choice.
  4430. then
  4431. Next := scan + REOpSz + RENextOffSz // Avoid recursion.
  4432. else
  4433. begin
  4434. repeat
  4435. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  4436. scan := regnext(scan);
  4437. until (scan = nil) or (PREOp(scan)^ <> OP_BRANCH);
  4438. Exit;
  4439. end;
  4440. end;
  4441. {$IFDEF ComplexBraces}
  4442. OP_LOOPENTRY:
  4443. begin //###0.925
  4444. //LoopStack [LoopStackIdx] := 0; //###0.940 line removed
  4445. FillFirstCharSet(Next); // execute LOOP
  4446. Exit;
  4447. end;
  4448. OP_LOOP,
  4449. OP_LOOPNG:
  4450. begin //###0.940
  4451. opnd := scan + PRENextOff(AlignToPtr(scan + REOpSz + RENextOffSz + REBracesArgSz * 2))^;
  4452. min_cnt := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^;
  4453. FillFirstCharSet(opnd);
  4454. if min_cnt = 0 then
  4455. FillFirstCharSet(Next);
  4456. Exit;
  4457. end;
  4458. {$ENDIF}
  4459. OP_STAR,
  4460. OP_STARNG: //###0.940
  4461. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  4462. OP_PLUS,
  4463. OP_PLUSNG:
  4464. begin //###0.940
  4465. FillFirstCharSet(scan + REOpSz + RENextOffSz);
  4466. Exit;
  4467. end;
  4468. OP_BRACES,
  4469. OP_BRACESNG:
  4470. begin //###0.940
  4471. opnd := scan + REOpSz + RENextOffSz + REBracesArgSz * 2;
  4472. min_cnt := PREBracesArg(AlignToPtr(scan + REOpSz + RENextOffSz))^; // BRACES
  4473. FillFirstCharSet(opnd);
  4474. if min_cnt > 0 then
  4475. Exit;
  4476. end;
  4477. OP_EEND:
  4478. begin
  4479. FirstCharSet := RegExprAllSet; //###0.948
  4480. Exit;
  4481. end;
  4482. else
  4483. begin
  4484. fLastErrorOpcode := Oper;
  4485. Error(reeUnknownOpcodeInFillFirst);
  4486. Exit;
  4487. end;
  4488. end; { of case scan^}
  4489. scan := Next;
  4490. end; { of while scan <> nil}
  4491. end; { of procedure FillFirstCharSet
  4492. --------------------------------------------------------------}
  4493. {$ENDIF}
  4494. procedure TRegExpr.InitCharCheckers;
  4495. var
  4496. Cnt: integer;
  4497. //
  4498. function Add(AChecker: TRegExprCharChecker): byte;
  4499. begin
  4500. Inc(Cnt);
  4501. if Cnt > High(CharCheckers) then
  4502. raise Exception.Create('Too small CharCheckers array');
  4503. CharCheckers[Cnt - 1] := AChecker;
  4504. Result := Cnt - 1;
  4505. end;
  4506. //
  4507. begin
  4508. Cnt := 0;
  4509. FillChar(CharCheckers, SizeOf(CharCheckers), 0);
  4510. CheckerIndex_Word := Add(CharChecker_Word);
  4511. CheckerIndex_NotWord := Add(CharChecker_NotWord);
  4512. CheckerIndex_Space := Add(CharChecker_Space);
  4513. CheckerIndex_NotSpace := Add(CharChecker_NotSpace);
  4514. CheckerIndex_Digit := Add(CharChecker_Digit);
  4515. CheckerIndex_NotDigit := Add(CharChecker_NotDigit);
  4516. CheckerIndex_VertSep := Add(CharChecker_VertSep);
  4517. CheckerIndex_NotVertSep := Add(CharChecker_NotVertSep);
  4518. CheckerIndex_HorzSep := Add(CharChecker_HorzSep);
  4519. CheckerIndex_NotHorzSep := Add(CharChecker_NotHorzSep);
  4520. //CheckerIndex_AllAZ := Add(CharChecker_AllAZ);
  4521. CheckerIndex_LowerAZ := Add(CharChecker_LowerAZ);
  4522. CheckerIndex_UpperAZ := Add(CharChecker_UpperAZ);
  4523. SetLength(CharCheckerInfos, 3);
  4524. with CharCheckerInfos[0] do
  4525. begin
  4526. CharBegin := 'a';
  4527. CharEnd:= 'z';
  4528. CheckerIndex := CheckerIndex_LowerAZ;
  4529. end;
  4530. with CharCheckerInfos[1] do
  4531. begin
  4532. CharBegin := 'A';
  4533. CharEnd := 'Z';
  4534. CheckerIndex := CheckerIndex_UpperAZ;
  4535. end;
  4536. with CharCheckerInfos[2] do
  4537. begin
  4538. CharBegin := '0';
  4539. CharEnd := '9';
  4540. CheckerIndex := CheckerIndex_Digit;
  4541. end;
  4542. end;
  4543. function TRegExpr.CharChecker_Word(ch: REChar): boolean;
  4544. begin
  4545. Result := IsWordChar(ch);
  4546. end;
  4547. function TRegExpr.CharChecker_NotWord(ch: REChar): boolean;
  4548. begin
  4549. Result := not IsWordChar(ch);
  4550. end;
  4551. function TRegExpr.CharChecker_Space(ch: REChar): boolean;
  4552. begin
  4553. Result := IsSpaceChar(ch);
  4554. end;
  4555. function TRegExpr.CharChecker_NotSpace(ch: REChar): boolean;
  4556. begin
  4557. Result := not IsSpaceChar(ch);
  4558. end;
  4559. function TRegExpr.CharChecker_Digit(ch: REChar): boolean;
  4560. begin
  4561. Result := IsDigitChar(ch);
  4562. end;
  4563. function TRegExpr.CharChecker_NotDigit(ch: REChar): boolean;
  4564. begin
  4565. Result := not IsDigitChar(ch);
  4566. end;
  4567. function TRegExpr.CharChecker_VertSep(ch: REChar): boolean;
  4568. begin
  4569. Result := IsLineSeparator(ch);
  4570. end;
  4571. function TRegExpr.CharChecker_NotVertSep(ch: REChar): boolean;
  4572. begin
  4573. Result := not IsLineSeparator(ch);
  4574. end;
  4575. function TRegExpr.CharChecker_HorzSep(ch: REChar): boolean;
  4576. begin
  4577. Result := IsHorzSeparator(ch);
  4578. end;
  4579. function TRegExpr.CharChecker_NotHorzSep(ch: REChar): boolean;
  4580. begin
  4581. Result := not IsHorzSeparator(ch);
  4582. end;
  4583. function TRegExpr.CharChecker_LowerAZ(ch: REChar): boolean;
  4584. begin
  4585. case ch of
  4586. 'a' .. 'z':
  4587. Result := True;
  4588. else
  4589. Result := False;
  4590. end;
  4591. end;
  4592. function TRegExpr.CharChecker_UpperAZ(ch: REChar): boolean;
  4593. begin
  4594. case ch of
  4595. 'A' .. 'Z':
  4596. Result := True;
  4597. else
  4598. Result := False;
  4599. end;
  4600. end;
  4601. {$IFDEF RegExpPCodeDump}
  4602. function TRegExpr.DumpOp(op: TREOp): RegExprString;
  4603. // printable representation of opcode
  4604. begin
  4605. case op of
  4606. OP_BOL:
  4607. Result := 'BOL';
  4608. OP_EOL:
  4609. Result := 'EOL';
  4610. OP_BOLML:
  4611. Result := 'BOLML';
  4612. OP_EOLML:
  4613. Result := 'EOLML';
  4614. OP_BOUND:
  4615. Result := 'BOUND'; // ###0.943
  4616. OP_NOTBOUND:
  4617. Result := 'NOTBOUND'; // ###0.943
  4618. OP_ANY:
  4619. Result := 'ANY';
  4620. OP_ANYML:
  4621. Result := 'ANYML'; // ###0.941
  4622. OP_ANYLETTER:
  4623. Result := 'ANYLETTER';
  4624. OP_NOTLETTER:
  4625. Result := 'NOTLETTER';
  4626. OP_ANYDIGIT:
  4627. Result := 'ANYDIGIT';
  4628. OP_NOTDIGIT:
  4629. Result := 'NOTDIGIT';
  4630. OP_ANYSPACE:
  4631. Result := 'ANYSPACE';
  4632. OP_NOTSPACE:
  4633. Result := 'NOTSPACE';
  4634. OP_ANYHORZSEP:
  4635. Result := 'ANYHORZSEP';
  4636. OP_NOTHORZSEP:
  4637. Result := 'NOTHORZSEP';
  4638. OP_ANYVERTSEP:
  4639. Result := 'ANYVERTSEP';
  4640. OP_NOTVERTSEP:
  4641. Result := 'NOTVERTSEP';
  4642. OP_ANYOF:
  4643. Result := 'ANYOF';
  4644. OP_ANYBUT:
  4645. Result := 'ANYBUT';
  4646. OP_ANYOFCI:
  4647. Result := 'ANYOF/CI';
  4648. OP_ANYBUTCI:
  4649. Result := 'ANYBUT/CI';
  4650. OP_BRANCH:
  4651. Result := 'BRANCH';
  4652. OP_EXACTLY:
  4653. Result := 'EXACTLY';
  4654. OP_EXACTLYCI:
  4655. Result := 'EXACTLY/CI';
  4656. OP_NOTHING:
  4657. Result := 'NOTHING';
  4658. OP_COMMENT:
  4659. Result := 'COMMENT';
  4660. OP_BACK:
  4661. Result := 'BACK';
  4662. OP_EEND:
  4663. Result := 'END';
  4664. OP_BSUBEXP:
  4665. Result := 'BSUBEXP';
  4666. OP_BSUBEXPCI:
  4667. Result := 'BSUBEXP/CI';
  4668. Succ(OP_OPEN) .. TREOp(Ord(OP_OPEN) + NSUBEXP - 1): // ###0.929
  4669. Result := Format('OPEN[%d]', [Ord(op) - Ord(OP_OPEN)]);
  4670. Succ(OP_CLOSE) .. TREOp(Ord(OP_CLOSE) + NSUBEXP - 1): // ###0.929
  4671. Result := Format('CLOSE[%d]', [Ord(op) - Ord(OP_CLOSE)]);
  4672. OP_STAR:
  4673. Result := 'STAR';
  4674. OP_PLUS:
  4675. Result := 'PLUS';
  4676. OP_BRACES:
  4677. Result := 'BRACES';
  4678. {$IFDEF ComplexBraces}
  4679. OP_LOOPENTRY:
  4680. Result := 'LOOPENTRY'; // ###0.925
  4681. OP_LOOP:
  4682. Result := 'LOOP'; // ###0.925
  4683. OP_LOOPNG:
  4684. Result := 'LOOPNG'; // ###0.940
  4685. {$ENDIF}
  4686. OP_STARNG:
  4687. Result := 'STARNG'; // ###0.940
  4688. OP_PLUSNG:
  4689. Result := 'PLUSNG'; // ###0.940
  4690. OP_BRACESNG:
  4691. Result := 'BRACESNG'; // ###0.940
  4692. else
  4693. Error(reeDumpCorruptedOpcode);
  4694. end; { of case op }
  4695. Result := ':' + Result;
  4696. end; { of function TRegExpr.DumpOp
  4697. -------------------------------------------------------------- }
  4698. function TRegExpr.Dump: RegExprString;
  4699. // dump a regexp in vaguely comprehensible form
  4700. var
  4701. s: PRegExprChar;
  4702. op: TREOp; // Arbitrary non-END op.
  4703. next: PRegExprChar;
  4704. i, NLen: integer;
  4705. Diff: PtrInt;
  4706. Ch: AnsiChar;
  4707. function PrintableChar(AChar: REChar): string; {$IFDEF InlineFuncs}inline;{$ENDIF}
  4708. begin
  4709. if AChar < ' ' then
  4710. Result := '#' + IntToStr(Ord(AChar))
  4711. else
  4712. Result := AChar;
  4713. end;
  4714. begin
  4715. if not IsProgrammOk then
  4716. Exit;
  4717. op := OP_EXACTLY;
  4718. Result := '';
  4719. s := programm + REOpSz;
  4720. while op <> OP_EEND do
  4721. begin // While that wasn't END last time...
  4722. op := s^;
  4723. Result := Result + Format('%2d%s', [s - programm, DumpOp(s^)]);
  4724. // Where, what.
  4725. next := regnext(s);
  4726. if next = nil // Next ptr.
  4727. then
  4728. Result := Result + ' (0)'
  4729. else
  4730. begin
  4731. if next > s
  4732. // ###0.948 PWideChar subtraction workaround (see comments in Tail method for details)
  4733. then
  4734. Diff := next - s
  4735. else
  4736. Diff := -(s - next);
  4737. Result := Result + Format(' (%d) ', [(s - programm) + Diff]);
  4738. end;
  4739. Inc(s, REOpSz + RENextOffSz);
  4740. if (op = OP_ANYOF) or (op = OP_ANYOFCI) or (op = OP_ANYBUT) or (op = OP_ANYBUTCI) then
  4741. begin
  4742. repeat
  4743. case s^ of
  4744. OpKind_End:
  4745. begin
  4746. Inc(s);
  4747. Break;
  4748. end;
  4749. OpKind_Range:
  4750. begin
  4751. Result := Result + 'Rng(';
  4752. Inc(s);
  4753. Result := Result + PrintableChar(s^) + '-';
  4754. Inc(s);
  4755. Result := Result + PrintableChar(s^);
  4756. Result := Result + ') ';
  4757. Inc(s);
  4758. end;
  4759. OpKind_MetaClass:
  4760. begin
  4761. Inc(s);
  4762. Result := Result + '\' + PrintableChar(s^) + ' ';
  4763. Inc(s);
  4764. end;
  4765. OpKind_Char:
  4766. begin
  4767. Inc(s);
  4768. NLen := PLongInt(s)^;
  4769. Inc(s, RENumberSz);
  4770. Result := Result + 'Ch(';
  4771. for i := 1 to NLen do
  4772. begin
  4773. Result := Result + PrintableChar(s^);
  4774. Inc(s);
  4775. end;
  4776. Result := Result + ') ';
  4777. end;
  4778. else
  4779. Error(reeDumpCorruptedOpcode);
  4780. end;
  4781. until false;
  4782. end;
  4783. if (op = OP_EXACTLY) or (op = OP_EXACTLYCI) then
  4784. begin
  4785. // Literal string, where present.
  4786. NLen := PLongInt(s)^;
  4787. Inc(s, RENumberSz);
  4788. for i := 1 to NLen do
  4789. begin
  4790. Result := Result + PrintableChar(s^);
  4791. Inc(s);
  4792. end;
  4793. end;
  4794. if (op = OP_BSUBEXP) or (op = OP_BSUBEXPCI) then
  4795. begin
  4796. Result := Result + ' \' + IntToStr(Ord(s^));
  4797. Inc(s);
  4798. end;
  4799. if (op = OP_BRACES) or (op = OP_BRACESNG) then
  4800. begin // ###0.941
  4801. // show min/max argument of braces operator
  4802. Result := Result + Format('{%d,%d}', [PREBracesArg(AlignToInt(s))^,
  4803. PREBracesArg(AlignToInt(s + REBracesArgSz))^]);
  4804. Inc(s, REBracesArgSz * 2);
  4805. end;
  4806. {$IFDEF ComplexBraces}
  4807. if (op = OP_LOOP) or (op = OP_LOOPNG) then
  4808. begin // ###0.940
  4809. Result := Result + Format(' -> (%d) {%d,%d}',
  4810. [(s - programm - (REOpSz + RENextOffSz)) +
  4811. PRENextOff(AlignToPtr(s + 2 * REBracesArgSz))^,
  4812. PREBracesArg(AlignToInt(s))^,
  4813. PREBracesArg(AlignToInt(s + REBracesArgSz))^]);
  4814. Inc(s, 2 * REBracesArgSz + RENextOffSz);
  4815. end;
  4816. {$ENDIF}
  4817. Result := Result + #$d#$a;
  4818. end; { of while }
  4819. // Header fields of interest.
  4820. if reganchored <> #0 then
  4821. Result := Result + 'Anchored; ';
  4822. if regmustString <> '' then
  4823. Result := Result + 'Must have: "' + regmustString + '"; ';
  4824. {$IFDEF UseFirstCharSet} // ###0.929
  4825. Result := Result + #$d#$a'First charset: ';
  4826. if FirstCharSet = [] then
  4827. Result := Result + '<empty set>'
  4828. else
  4829. if FirstCharSet = RegExprAllSet then
  4830. Result := Result + '<all chars>'
  4831. else
  4832. for Ch := #0 to #255 do
  4833. if byte(Ch) in FirstCharSet then
  4834. begin
  4835. if Ch < ' ' then
  4836. Result := Result + PrintableChar(Ch) // ###0.948
  4837. else
  4838. Result := Result + Ch;
  4839. end;
  4840. {$ENDIF}
  4841. Result := Result + #$d#$a;
  4842. end; { of function TRegExpr.Dump
  4843. -------------------------------------------------------------- }
  4844. {$ENDIF}
  4845. {$IFDEF reRealExceptionAddr}
  4846. {$OPTIMIZATION ON}
  4847. // ReturnAddr works correctly only if compiler optimization is ON
  4848. // I placed this method at very end of unit because there are no
  4849. // way to restore compiler optimization flag ...
  4850. {$ENDIF}
  4851. procedure TRegExpr.Error(AErrorID: integer);
  4852. {$IFDEF reRealExceptionAddr}
  4853. function ReturnAddr: Pointer; // ###0.938
  4854. asm
  4855. mov eax,[ebp+4]
  4856. end;
  4857. {$ENDIF}
  4858. var
  4859. e: ERegExpr;
  4860. begin
  4861. fLastError := AErrorID; // dummy stub - useless because will raise exception
  4862. if AErrorID < 1000 // compilation error ?
  4863. then
  4864. e := ERegExpr.Create(ErrorMsg(AErrorID) // yes - show error pos
  4865. + ' (pos ' + IntToStr(CompilerErrorPos) + ')')
  4866. else
  4867. e := ERegExpr.Create(ErrorMsg(AErrorID));
  4868. e.ErrorCode := AErrorID;
  4869. e.CompilerErrorPos := CompilerErrorPos;
  4870. raise e
  4871. {$IFDEF reRealExceptionAddr}
  4872. at ReturnAddr; // ###0.938
  4873. {$ENDIF}
  4874. end; { of procedure TRegExpr.Error
  4875. -------------------------------------------------------------- }
  4876. (*
  4877. PCode persistence:
  4878. FirstCharSet
  4879. programm, regsize
  4880. reganchored // -> programm
  4881. regmust, regmustlen // -> programm
  4882. fExprIsCompiled
  4883. *)
  4884. // be carefull - placed here code will be always compiled with
  4885. // compiler optimization flag
  4886. initialization
  4887. RegExprInvertCaseFunction := TRegExpr.InvertCaseFunction;
  4888. end.