| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- /*
- --------------------------------------------------------------------------------------------------------------------------------
- Original
- --------------------------------------------------------------------------------------------------------------------------------
- Pattern ::= Disjunction
- Disjunction ::= Alternative | Alternative '|' Disjunction
- Alternative ::= [empty] | Alternative Term
- Term ::= Assertion | Atom | Atom Quantifier
- Assertion ::= '^' | '$' | '\' 'b' | '\' 'B' | '(' '?' '=' Disjunction ')' | '(' '?' '!' Disjunction ')'
- Quantifier ::= QuantifierPrefix | QuantifierPrefix '?'
- QuantifierPrefix ::= '*' | '+' | '?' | '{' DecimalDigits '}' | '{' DecimalDigits ',' '}' | '{' DecimalDigits ',' DecimalDigits '}'
- Atom ::= PatternCharacter | '.' | '\' AtomEscape | CharacterClass | '(' Disjunction ')' | '(' '?' ':' Disjunction ')'
- PatternCharacter ::= SourceCharacter but not any of { '^', '$', '\', '.', '*', '+', '?', '(', ')', '[', ']', '{', '}', '|' }
- AtomEscape ::= DecimalEscape | CharacterEscape | CharacterClassEscape
- CharacterEscape ::= ControlEscape | 'c' ControlLetter | HexEscapeSequence | UnicodeEscapeSequence | IdentityEscape
- ControlEscape ::= one of { 'f', 'n', 'r', 't', 'v' }
- ControlLetter ::= one of { 'a'..'z', 'A'..'Z' }
- IdentityEscape ::= (SourceCharacter but not IdentifierPart) | <ZWJ> | <ZWNJ>
- DecimalEscape ::= DecimalIntegerLiteral [lookahead not in DecimalDigit]
- CharacterClassEscape :: one of { 'd', 'D', 's', 'S', 'w', 'W' }
- CharacterClass ::= '[' [lookahead not in {'^'}] ClassRanges ']' | '[' '^' ClassRanges ']'
- ClassRanges ::= [empty] | NonemptyClassRanges
- NonemptyClassRanges ::= ClassAtom | ClassAtom NonemptyClassRangesNoDash | ClassAtom '-' ClassAtom ClassRanges
- NonemptyClassRangesNoDash ::= ClassAtom | ClassAtomNoDash NonemptyClassRangesNoDash | ClassAtomNoDash '-' ClassAtom ClassRanges
- ClassAtom ::= '-' | ClassAtomNoDash
- ClassAtomNoDash ::= SourceCharacter but not one of { '\', ']', '-' } | '\' ClassEscape
- ClassEscape ::= DecimalEscape | 'b' | CharacterEscape | CharacterClassEscape
- SourceCharacter ::= <unicode character>
- HexEscapeSequence ::= 'x' HexDigit HexDigit
- UnicodeEscapeSequence ::= 'u' HexDigit HexDigit HexDigit HexDigit | 'u' '{' HexDigits '}'
- HexDigit ::= one of { '0'..'9', 'a'..'f', 'A'..'F' }
- IdentifierStart ::= UnicodeLetter | '$' | '_' | '\' UnicodeEscapeSequence
- IdentifierPart ::= IdentifierStart | UnicodeCombiningMark | UnicodeDigit | UnicodeConnectorPunctuation | <ZWNJ> | <ZWJ>
- UnicodeLetter ::= <unicode Uppercase letter> | <unicode Lowercase letter> | <unicode Titlecase letter> | <unicode Modifier letter> | <unicode Other letter> | <unicode Letter number>
- UnicodeCombiningMark = <unicode Non-spacing mark> | <unicode combining spacing mark>
- UnicodeDigit ::= <unicode Decimal number>
- UnicodeConnectorPunctuation ::= <unicode Connector punctuation>
- DecimalIntegerLiteral ::= '0' | NonZeroDigit DecimalDigits?
- DecimalDigits ::= DecimalDigit | DecimalDigits DecimalDigit
- DecimalDigit ::= one of { '0'..'9' }
- NonZeroDigit ::= one of { '1'..'9' }
- ------------------
- Annex B Deviations
- ------------------
- 1. The assertions (?= ) and (?! ) are treated as though they have a surrounding non-capture group, and hence can be quantified.
- Other assertions are not quantifiable.
- QuantifiableAssertion [added] ::= '(' '?' '=' Disjunction ')' | '(' '?' '!' Disjunction ')'
- Assertion [replaced] ::= '^' | '$' | '\' 'b' | '\' 'B' | QuantifiableAssertion
- Term ::= ... | QuantifiableAssertion Quantifier [added]
- --------------------------------------------------------------------------------------------------------------------------------
- Left factored
- --------------------------------------------------------------------------------------------------------------------------------
- Pattern ::= Disjunction
- Disjunction ::= Alternative ('|' Alternative)*
- FOLLOW(Disjunction) = { <eof>, ')' }
- Alternative ::= Term*
- FOLLOW(Alternative) = { <eof>, ')', '|' }
- Term ::= ( '^' | '$' | '\' 'b' | '\' 'B' | '(' '?' '=' Disjunction ')' | '(' '?' '!' Disjunction ')' | Atom Quantifier? )
- | ( PatternCharacter | '.' | '\' AtomEscape | CharacterClass | '(' Disjunction ')' | '(' '?' ':' Disjunction ')' )
- ( '*' | '+' | '?' | '{' DecimalDigits (',' DecimalDigits?)? '}' ) '?'?
- PatternCharacter ::= <unicode character> but not any of { '^', '$', '\', '.', '*', '+', '?', '(', ')', '[', ']', '{', '}', '|' }
- AtomEscape ::= DecimalEscape | CharacterEscape | CharacterClassEscape
- CharacterEscape ::= ControlEscape | 'c' ControlLetter | HexEscapeSequence | UnicodeEscapeSequence | IdentityEscape
- ControlEscape ::= one of { 'f', 'n', 'r', 't', 'v' }
- ControlLetter ::= one of { 'a'..'z', 'A'..'Z' }
- IdentityEscape ::= <unicode character> but not <unicode Uppercase letter>, <unicode Lowercase letter>, <unicode Titlecase letter>, <unicode Modifier letter>, <unicode Other letter>, <unicode Letter number>, '$', '_', <unicode Non-spacing mark>, <unicode combining spacing mark>, <unicode Decimal number>, <unicode Connector punctuation>
- DecimalEscape ::= DecimalIntegerLiteral [lookahead not in DecimalDigit]
- CharacterClassEscape :: one of { 'd', 'D', 's', 'S', 'w', 'W' }
- CharacterClass ::= '[' [lookahead not in {'^'}] ClassRanges ']' | '[' '^' ClassRanges ']'
- ClassRanges ::= [empty] | NonemptyClassRanges
- NonemptyClassRanges ::= ClassAtom | ClassAtom NonemptyClassRangesNoDash | ClassAtom '-' ClassAtom ClassRanges
- NonemptyClassRangesNoDash ::= ClassAtom | ClassAtomNoDash NonemptyClassRangesNoDash | ClassAtomNoDash '-' ClassAtom ClassRanges
- ClassAtom ::= '-' | ClassAtomNoDash
- ClassAtomNoDash ::= SourceCharacter but not one of { '\', ']', '-' } | '\' ClassEscape
- ClassEscape ::= DecimalEscape | 'b' | CharacterEscape | CharacterClassEscape
- HexEscapeSequence ::= 'x' HexDigit{2}
- UnicodeEscapeSequence ::= 'u' HexDigit{4} | 'u' '{' HexDigits{4-6} '}'
- HexDigit ::= one of { '0'..'9', 'a'..'f', 'A'..'F' }
- DecimalIntegerLiteral ::= '0' | NonZeroDigit DecimalDigits?
- DecimalDigits ::= DecimalDigit+
- DecimalDigit ::= one of { '0'..'9' }
- NonZeroDigit ::= one of { '1'..'9' }
- ------------------
- Annex B Deviations
- ------------------
- QuantifiableAssertion [added] ::= '(' '?' '=' Disjunction ')' | '(' '?' '!' Disjunction ')'
- Term ::= ... | '(' '?' '=' Disjunction ')' [removed] | '(' '?' '!' Disjunction ')' [removed] | QuantifiableAssertion Quantifier? [added]
- */
- #include "ParserPch.h"
- namespace UnifiedRegex
- {
- template <typename P, const bool IsLiteral>
- const CharCount Parser<P, IsLiteral>::initLitbufSize;
- ParseError::ParseError(bool isBody, CharCount pos, CharCount encodedPos, HRESULT error)
- : isBody(isBody), pos(pos), encodedPos(encodedPos), error(error)
- {
- }
- template <typename P, const bool IsLiteral>
- Parser<P, IsLiteral>::Parser
- ( Js::ScriptContext* scriptContext
- , ArenaAllocator* ctAllocator
- , StandardChars<EncodedChar>* standardEncodedChars
- , StandardChars<Char>* standardChars
- , bool isUtf8
- #if ENABLE_REGEX_CONFIG_OPTIONS
- , DebugWriter* w
- #endif
- )
- : scriptContext(scriptContext)
- , ctAllocator(ctAllocator)
- , standardEncodedChars(standardEncodedChars)
- , standardChars(standardChars)
- #if ENABLE_REGEX_CONFIG_OPTIONS
- , w(w)
- #endif
- , input(0)
- , inputLim(0)
- , next(0)
- , inBody(false)
- , numGroups(1)
- , nextGroupId(1) // implicit overall group always takes index 0
- , litbuf(0)
- , litbufLen(0)
- , litbufNext(0)
- , surrogatePairList(nullptr)
- , currentSurrogatePairNode(nullptr)
- , tempLocationOfSurrogatePair(nullptr)
- , tempLocationOfRange(nullptr)
- , codePointAtTempLocation(0)
- , unicodeFlagPresent(false)
- , dotAllFlagPresent(false)
- , caseInsensitiveFlagPresent(false)
- , positionAfterLastSurrogate(nullptr)
- , valueOfLastSurrogate(INVALID_CODEPOINT)
- , deferredIfNotUnicodeError(nullptr)
- , deferredIfUnicodeError(nullptr)
- {
- this->SetIsUtf8(isUtf8);
- }
- //
- // Input buffer management
- //
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::SetPosition(const EncodedChar* input, const EncodedChar* inputLim, bool inBody)
- {
- this->input = input;
- this->inputLim = inputLim;
- next = input;
- this->inBody = inBody;
- this->RestoreMultiUnits(0);
- }
- template <typename P, const bool IsLiteral>
- inline CharCount Parser<P, IsLiteral>::Pos()
- {
- CharCount nextOffset = Chars<EncodedChar>::OSB(next, input);
- Assert(nextOffset >= this->m_cMultiUnits);
- return nextOffset - (CharCount) this->m_cMultiUnits;
- }
- template <typename P, const bool IsLiteral>
- inline bool Parser<P, IsLiteral>::IsEOF()
- {
- return next >= inputLim;
- }
- template <typename P, const bool IsLiteral>
- inline bool Parser<P, IsLiteral>::ECCanConsume(CharCount n /*= 1*/)
- {
- return next + n <= inputLim;
- }
- template <typename P, const bool IsLiteral>
- inline typename P::EncodedChar Parser<P, IsLiteral>::ECLookahead(CharCount n /*= 0*/)
- {
- // Ok to look ahead to terminating 0
- Assert(next + n <= inputLim);
- return next[n];
- }
- template <typename P, const bool IsLiteral>
- inline typename P::EncodedChar Parser<P, IsLiteral>::ECLookback(CharCount n /*= 0*/)
- {
- // Ok to look ahead to terminating 0
- Assert(n + input <= next);
- return *(next - n);
- }
- template <typename P, const bool IsLiteral>
- inline void Parser<P, IsLiteral>::ECConsume(CharCount n /*= 1*/)
- {
- Assert(next + n <= inputLim);
- #if DBG
- for (CharCount i = 0; i < n; i++)
- Assert(!this->IsMultiUnitChar(next[i]));
- #endif
- next += n;
- }
- template <typename P, const bool IsLiteral>
- inline void Parser<P, IsLiteral>::ECConsumeMultiUnit(CharCount n /*= 1*/)
- {
- Assert(next + n <= inputLim);
- next += n;
- }
- template <typename P, const bool IsLiteral>
- inline void Parser<P, IsLiteral>::ECRevert(CharCount n /*= 1*/)
- {
- Assert(n + input <= next);
- next -= n;
- }
- //
- // Helpers
- //
- template <typename P, const bool IsLiteral>
- int Parser<P, IsLiteral>::TryParseExtendedUnicodeEscape(Char& c, bool& previousSurrogatePart, bool trackSurrogatePair /* = false */)
- {
- if (!scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- return 0;
- }
- if (!ECCanConsume(2) || ECLookahead(0) !='{' || !standardEncodedChars->IsHex(ECLookahead(1)))
- {
- return 0;
- }
- // The first character is mandatory to consume escape sequence, so we check for it above, at this stage we can set it as we already checked.
- codepoint_t codePoint = standardEncodedChars->DigitValue(ECLookahead(1));
- int i = 2;
- while(ECCanConsume(i + 1) && standardEncodedChars->IsHex(ECLookahead(i)))
- {
- codePoint <<= 4;
- codePoint += standardEncodedChars->DigitValue(ECLookahead(i));
- if (codePoint > 0x10FFFF)
- {
- DeferredFailIfUnicode(JSERR_RegExpInvalidEscape);
- return 0;
- }
- i++;
- }
- if(!ECCanConsume(i + 1) || ECLookahead(i) != '}')
- {
- return 0;
- }
- uint consumptionNumber = i + 1;
- Assert(consumptionNumber >= 3);
- if (!previousSurrogatePart && trackSurrogatePair && this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- // Current location
- TrackIfSurrogatePair(codePoint, (next - 1), consumptionNumber + 1);
- }
- char16 other;
- // Generally if this code point is a single character, then we take it and return.
- // If the character is made up of two characters then we emit the first and backtrack to the start of th escape sequence;
- // Following that we check if we have already seen the first character, and if so emit the second and consume the entire escape sequence.
- if (codePoint < 0x10000)
- {
- c = UTC(codePoint);
- ECConsumeMultiUnit(consumptionNumber);
- }
- else if (previousSurrogatePart)
- {
- previousSurrogatePart = false;
- Js::NumberUtilities::CodePointAsSurrogatePair(codePoint, &other, &c);
- ECConsumeMultiUnit(consumptionNumber);
- }
- else
- {
- previousSurrogatePart = true;
- Js::NumberUtilities::CodePointAsSurrogatePair(codePoint, &c, &other);
- Assert(ECLookback(1) == 'u' && ECLookback(2) == '\\');
- ECRevert(2);
- }
- return consumptionNumber;
- }
- // This function has the following 'knowledge':
- // - A codepoint that might be part of a surrogate pair or part of one
- // - A location where that codepoint is located
- // - Previously tracked part of surrogate pair (tempLocationOfSurrogatePair, and codePointAtTempLocation), which is always a surrogate lower part.
- // - A pointer to the current location of the linked list
- // - If a previous location is tracked, then it is of a parsed character (not given character) before current, and not same to current
- // This can't be asserted directly, and has to be followed by callers. Term pass can reset with each iteration, as well as this method in cases it needs.
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>:: TrackIfSurrogatePair(codepoint_t codePoint, const EncodedChar* location, uint32 consumptionLength)
- {
- Assert(codePoint < 0x110000);
- Assert(location != nullptr);
- Assert(location != this->tempLocationOfSurrogatePair);
- if (Js::NumberUtilities::IsSurrogateLowerPart(codePoint))
- {
- this->tempLocationOfSurrogatePair = location;
- this->codePointAtTempLocation = codePoint;
- }
- else
- {
- if(Js::NumberUtilities::IsSurrogateUpperPart(codePoint) && this->tempLocationOfSurrogatePair != nullptr)
- {
- Assert(Js::NumberUtilities::IsSurrogateLowerPart(codePointAtTempLocation));
- consumptionLength = (uint32)(location - this->tempLocationOfSurrogatePair) + consumptionLength;
- codePoint = Js::NumberUtilities::SurrogatePairAsCodePoint(codePointAtTempLocation, codePoint);
- location = this->tempLocationOfSurrogatePair;
- }
- // At this point we can clear previous location, and then if codePoint is bigger than 0xFFFF store it, as we either received it or combined it above
- this->tempLocationOfSurrogatePair = nullptr;
- this->codePointAtTempLocation = 0;
- }
- if (codePoint > 0xFFFF)
- {
- this->positionAfterLastSurrogate = location + consumptionLength;
- this->valueOfLastSurrogate = codePoint;
- // When parsing without AST we aren't given an allocator. In addition, only the 2 lines above are used during Pass 0;
- // while the bottom is used during Pass 1 (which isn't done when ParseNoAST)
- if(this->ctAllocator != nullptr)
- {
- SurrogatePairTracker* node = Anew(this->ctAllocator, SurrogatePairTracker, location, this->tempLocationOfRange, codePoint, consumptionLength, this->m_cMultiUnits);
- if (surrogatePairList == nullptr)
- {
- Assert(currentSurrogatePairNode == nullptr);
- surrogatePairList = node;
- currentSurrogatePairNode = node;
- }
- else
- {
- Assert(currentSurrogatePairNode != nullptr);
- currentSurrogatePairNode->next = node;
- currentSurrogatePairNode = node;
- }
- }
- }
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::CreateSurrogatePairAtom(char16 lower, char16 upper)
- {
- MatchLiteralNode * literalNode = Anew(this->ctAllocator, MatchLiteralNode, 0, 0);
- MatchCharNode lowerNode(lower);
- MatchCharNode upperNode(upper);
- AccumLiteral(literalNode, &lowerNode);
- AccumLiteral(literalNode, &upperNode);
- return literalNode;
- }
- // This function will create appropriate pairs of ranges and add them to the disjunction node.
- // Terms used in comments:
- // - A minor codePoint is smaller than major codePoint, and both define a range of codePoints above 0x10000; to avoid confusion between lower/upper denoting codeUnits composing the surrogate pair.
- // - A boundary is a mod 0x400 alignment marker due to the nature of surrogate pairs representation. So the codepoint 0x10300 lies between boundaries 0x10000 and 0x10400.
- // - A prefix is the range set used to represent the values from minorCodePoint to the first boundary above minorCodePoint if applicable.
- // - A suffix is the range set used to represent the values from first boundary below majorCodePoint to the majorCodePoint if applicable.
- // - A full range is the range set used to represent the values from first boundary above minorCodePoint to first boundary below majorCodePoint if applicable.
- // The algorithm works as follows:
- // 1. Determine minorBoundary (minorCodePoint - mod 0x400 +0x400) and majorBoundary (majorCodePoint - mod 0x400). minorBoundary > minorCodePoint and majorBoundary < majorCodePoint
- // 2. Based on the codePoints and the boundaries, prefix, suffix, and full range is determined. Here are the rules:
- // 2-a. If minorBoundary > majorBoundary, we have an inner boundary range, output just that.
- // 2-b. If minorBoundary - 0x400u != minorCodepoint (i.e. codePoint doesn't lie right on a boundary to be part of a full range) we have a prefix.
- // 2-c. If majorBoundary + 0x3FFu != majorCodepoint (i.e. codePoint doesn't lie right before a boundary to be part of a full range) we have a suffix.
- // 2-d. We have a full range, if the two boundaries don't equal, OR the codePoints lie on the range boundaries opposite to what constitutes a prefix/suffix.
- // Visual representation for sample range 0x10300 - 0x10900
- // | [ _ | ^ ] |
- // 0x10000 0x10800 0x11000
- // [ ] - denote the actual range
- // _ - minorBoundary
- // ^ - majorBoundary
- // | - other boundaries
- // prefix is between [ and _
- // suffix is between ^ and ]
- // full range is between _ and ^
- template <typename P, const bool IsLiteral>
- AltNode* Parser<P, IsLiteral>::AppendSurrogateRangeToDisjunction(codepoint_t minorCodePoint, codepoint_t majorCodePoint, AltNode *lastAltNode)
- {
- Assert(minorCodePoint < majorCodePoint);
- Assert(minorCodePoint >= 0x10000u);
- Assert(majorCodePoint >= 0x10000u);
- Assert(scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled() && unicodeFlagPresent);
- char16 lowerMinorCodeUnit, upperMinorCodeUnit, lowerMajorCodeUnit, upperMajorCodeUnit;
- Js::NumberUtilities::CodePointAsSurrogatePair(minorCodePoint, &lowerMinorCodeUnit, &upperMinorCodeUnit);
- Js::NumberUtilities::CodePointAsSurrogatePair(majorCodePoint, &lowerMajorCodeUnit, &upperMajorCodeUnit);
- // These boundaries represent whole range boundaries, as in 0x10000, 0x10400, 0x10800 etc
- // minor boundary is the first boundary strictly above minorCodePoint
- // major boundary is the first boundary below or equal to majorCodePoint
- codepoint_t minorBoundary = minorCodePoint - (minorCodePoint % 0x400u) + 0x400u;
- codepoint_t majorBoundary = majorCodePoint - (majorCodePoint % 0x400u);
- Assert(minorBoundary >= 0x10000);
- Assert(majorBoundary >= 0x10000);
- AltNode* tailToAdd = nullptr;
- // If the minor boundary is higher than major boundary, that means we have a range within the boundary and is less than 0x400
- // Ex: 0x10430 - 0x10700 will have minor boundary of 0x10800 and major of 0x10400
- // This pair will be represented in single range set.
- const bool singleRange = minorBoundary > majorBoundary;
- if (singleRange)
- {
- Assert(majorCodePoint - minorCodePoint < 0x400u);
- Assert(lowerMinorCodeUnit == lowerMajorCodeUnit);
- MatchCharNode* lowerCharNode = Anew(ctAllocator, MatchCharNode, lowerMinorCodeUnit);
- MatchSetNode* setNode = Anew(ctAllocator, MatchSetNode, false, false);
- setNode->set.SetRange(ctAllocator, (Char)upperMinorCodeUnit, (Char)upperMajorCodeUnit);
- ConcatNode* concatNode = Anew(ctAllocator, ConcatNode, lowerCharNode, Anew(ctAllocator, ConcatNode, setNode, nullptr));
- tailToAdd = Anew(ctAllocator, AltNode, concatNode, nullptr);
- }
- else
- {
- Node* prefixNode = nullptr, *suffixNode = nullptr;
- const bool twoConsecutiveRanges = minorBoundary == majorBoundary;
- // For minorBoundary,
- if (minorBoundary - minorCodePoint == 1) // Single character in minor range
- {
- // The prefix is only a surrogate pair atom
- prefixNode = CreateSurrogatePairAtom(lowerMinorCodeUnit, upperMinorCodeUnit);
- }
- else if (minorCodePoint != minorBoundary - 0x400u) // Minor range isn't full
- {
- Assert(minorBoundary - minorCodePoint < 0x400u);
- MatchCharNode* lowerCharNode = Anew(ctAllocator, MatchCharNode, (Char)lowerMinorCodeUnit);
- MatchSetNode* upperSetNode = Anew(ctAllocator, MatchSetNode, false);
- upperSetNode->set.SetRange(ctAllocator, (Char)upperMinorCodeUnit, (Char)0xDFFFu);
- prefixNode = Anew(ctAllocator, ConcatNode, lowerCharNode, Anew(ctAllocator, ConcatNode, upperSetNode, nullptr));
- }
- else // Full minor range
- {
- minorBoundary -= 0x400u;
- }
- if (majorBoundary == majorCodePoint) // Single character in major range
- {
- // The suffix is only a surrogate pair atom
- suffixNode = CreateSurrogatePairAtom(lowerMajorCodeUnit, upperMajorCodeUnit);
- majorBoundary -= 0x400u;
- }
- else if (majorBoundary + 0x3FFu != majorCodePoint) // Major range isn't full
- {
- Assert(majorCodePoint - majorBoundary < 0x3FFu);
- MatchCharNode* lowerCharNode = Anew(ctAllocator, MatchCharNode, (Char)lowerMajorCodeUnit);
- MatchSetNode* upperSetNode = Anew(ctAllocator, MatchSetNode, false, false);
- upperSetNode->set.SetRange(ctAllocator, (Char)0xDC00u, (Char)upperMajorCodeUnit);
- suffixNode = Anew(ctAllocator, ConcatNode, lowerCharNode, Anew(ctAllocator, ConcatNode, upperSetNode, nullptr));
- majorBoundary -= 0x400u;
- }
- const bool nonFullConsecutiveRanges = twoConsecutiveRanges && prefixNode != nullptr && suffixNode != nullptr;
- if (nonFullConsecutiveRanges)
- {
- Assert(suffixNode != nullptr);
- Assert(minorCodePoint != minorBoundary - 0x400u);
- Assert(majorBoundary + 0x3FFu != majorCodePoint);
- // If the minor boundary is equal to major boundary, that means we have a cross boundary range that only needs 2 nodes for prefix/suffix.
- // We can only cross one boundary.
- Assert(majorCodePoint - minorCodePoint < 0x800u);
- tailToAdd = Anew(ctAllocator, AltNode, prefixNode, Anew(ctAllocator, AltNode, suffixNode, nullptr));
- }
- else
- {
- // We have 3 sets of ranges, comprising of prefix, full and suffix.
- Assert(majorCodePoint - minorCodePoint >= 0x400u);
- Assert((prefixNode != nullptr && suffixNode != nullptr) // Spanning more than two ranges
- || (prefixNode == nullptr && minorBoundary == minorCodePoint) // Two consecutive ranges and the minor is full
- || (suffixNode == nullptr && majorBoundary + 0x3FFu == majorCodePoint)); // Two consecutive ranges and the major is full
- Node* lowerOfFullRange;
- char16 lowerMinorBoundary, lowerMajorBoundary, ignore;
- Js::NumberUtilities::CodePointAsSurrogatePair(minorBoundary, &lowerMinorBoundary, &ignore);
- bool singleFullRange = majorBoundary == minorBoundary;
- if (singleFullRange)
- {
- // The lower part of the full range is simple a surrogate lower char
- lowerOfFullRange = Anew(ctAllocator, MatchCharNode, (Char)lowerMinorBoundary);
- }
- else
- {
- Js::NumberUtilities::CodePointAsSurrogatePair(majorBoundary, &lowerMajorBoundary, &ignore);
- MatchSetNode* setNode = Anew(ctAllocator, MatchSetNode, false, false);
- setNode->set.SetRange(ctAllocator, (Char)lowerMinorBoundary, (Char)lowerMajorBoundary);
- lowerOfFullRange = setNode;
- }
- MatchSetNode* fullUpperRange = Anew(ctAllocator, MatchSetNode, false, false);
- fullUpperRange->set.SetRange(ctAllocator, (Char)0xDC00u, (Char)0xDFFFu);
- // These are added in the following order [full] [prefix][suffix]
- // This is doing by prepending, so in reverse.
- if (suffixNode != nullptr)
- {
- tailToAdd = Anew(ctAllocator, AltNode, suffixNode, tailToAdd);
- }
- if (prefixNode != nullptr)
- {
- tailToAdd = Anew(ctAllocator, AltNode, prefixNode, tailToAdd);
- }
- tailToAdd = Anew(ctAllocator, AltNode, Anew(ctAllocator, ConcatNode, lowerOfFullRange, Anew(ctAllocator, ConcatNode, fullUpperRange, nullptr)), tailToAdd);
- }
- }
- if (lastAltNode != nullptr)
- {
- Assert(lastAltNode->tail == nullptr);
- lastAltNode->tail = tailToAdd;
- }
- return tailToAdd;
- }
- template <typename P, const bool IsLiteral>
- AltNode* Parser<P, IsLiteral>::AppendSurrogatePairToDisjunction(codepoint_t codePoint, AltNode *lastAltNode)
- {
- char16 lower, upper;
- Js::NumberUtilities::CodePointAsSurrogatePair(codePoint, &lower, &upper);
- AltNode* tailNode = Anew(ctAllocator, AltNode, CreateSurrogatePairAtom(lower, upper), nullptr);
- if (lastAltNode != nullptr)
- {
- lastAltNode->tail = tailNode;
- }
- return tailNode;
- }
- //
- // Errors
- //
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::Fail(HRESULT error)
- {
- throw ParseError(inBody, Pos(), Chars<EncodedChar>::OSB(next, input), error);
- }
- // This doesn't throw, but stores first error code for throwing later
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::DeferredFailIfUnicode(HRESULT error)
- {
- Assert(this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled());
- if (this->deferredIfUnicodeError == nullptr)
- {
- this->deferredIfUnicodeError = Anew(ctAllocator, ParseError, inBody, Pos(), Chars<EncodedChar>::OSB(next, input), error);
- }
- }
- // This doesn't throw, but stores first error code for throwing later
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::DeferredFailIfNotUnicode(HRESULT error)
- {
- Assert(this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled());
- if (this->deferredIfNotUnicodeError == nullptr)
- {
- this->deferredIfNotUnicodeError = Anew(ctAllocator, ParseError, inBody, Pos(), Chars<EncodedChar>::OSB(next, input), error);
- }
- }
- template <typename P, const bool IsLiteral>
- inline void Parser<P, IsLiteral>::ECMust(EncodedChar ec, HRESULT error)
- {
- // We never look for 0
- Assert(ec != 0);
- if (ECLookahead() != ec)
- Fail(error);
- ECConsume();
- }
- template <typename P, const bool IsLiteral>
- inline char16 Parser<P, IsLiteral>::NextChar()
- {
- Assert(!IsEOF());
- // Could be an embedded 0
- Char c = this->template ReadFull<true>(next, inputLim);
- // No embedded newlines in literals
- if (IsLiteral && standardChars->IsNewline(c))
- Fail(ERRnoSlash);
- return c;
- }
- //
- // Patterns/Disjunctions/Alternatives
- //
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::PatternPass0()
- {
- this->positionAfterLastSurrogate = nullptr;
- this->deferredIfNotUnicodeError = nullptr;
- this->deferredIfUnicodeError = nullptr;
- DisjunctionPass0(0);
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::PatternPass1()
- {
- return DisjunctionPass1();
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::UnionNodes(Node* prev, Node* curr)
- {
- if (prev->tag == Node::MatchChar)
- {
- MatchCharNode* prevChar = (MatchCharNode*)prev;
- if (curr->tag == Node::MatchChar)
- {
- MatchCharNode* currChar = (MatchCharNode*)curr;
- if (prevChar->cs[0] == currChar->cs[0])
- // Just ignore current node
- return prevChar;
- else
- {
- // Union chars into new set
- MatchSetNode* setNode = Anew(ctAllocator, MatchSetNode, false);
- setNode->set.Set(ctAllocator, prevChar->cs[0]);
- setNode->set.Set(ctAllocator, currChar->cs[0]);
- return setNode;
- }
- }
- else if (curr->tag == Node::MatchSet)
- {
- MatchSetNode* currSet = (MatchSetNode*)curr;
- if (currSet->isNegation)
- // Can't merge
- return 0;
- else
- {
- // Union chars into new set
- MatchSetNode* setNode = Anew(ctAllocator, MatchSetNode, false);
- setNode->set.Set(ctAllocator, prevChar->cs[0]) ;
- setNode->set.UnionInPlace(ctAllocator, currSet->set);
- return setNode;
- }
- }
- else
- // Can't merge
- return 0;
- }
- else if (prev->tag == Node::MatchSet)
- {
- MatchSetNode* prevSet = (MatchSetNode*)prev;
- if (prevSet->isNegation)
- // Can't merge
- return 0;
- else if (curr->tag == Node::MatchChar)
- {
- MatchCharNode* currChar = (MatchCharNode*)curr;
- // Include char in prev set
- prevSet->set.Set(ctAllocator, currChar->cs[0]);
- return prevSet;
- }
- else if (curr->tag == Node::MatchSet)
- {
- MatchSetNode* currSet = (MatchSetNode*)curr;
- if (currSet->isNegation)
- // Can't merge
- return 0;
- else
- {
- // Include chars in prev set
- prevSet->set.UnionInPlace(ctAllocator, currSet->set);
- return prevSet;
- }
- }
- else
- // Can't merge
- return 0;
- }
- else
- // Can't merge
- return 0;
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::DisjunctionPass0(int depth)
- {
- AlternativePass0(depth);
- while (true)
- {
- // Could be terminating 0
- if (ECLookahead() != '|')
- return;
- ECConsume();
- AlternativePass0(depth);
- }
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::DisjunctionPass1()
- {
- // Maintain the invariants:
- // - alt lists have two or more items
- // - alt list items are never alt lists (so we must inline them)
- // - an alt list never contains two consecutive match-character/match-set nodes
- // (so we must union consecutive items into a single set)
- Node* node = AlternativePass1();
- AltNode* last = 0;
- // First node may be an alternative
- if (node->tag == Node::Alt)
- {
- last = (AltNode*)node;
- while (last->tail != 0)
- last = last->tail;
- }
- while (true)
- {
- // Could be terminating 0
- if (ECLookahead() != '|')
- return node;
- ECConsume(); // '|'
- Node* next = AlternativePass1();
- AnalysisAssert(next != nullptr);
- Node* revisedPrev = UnionNodes(last == 0 ? node : last->head, next);
- if (revisedPrev != 0)
- {
- // Can merge next into previously seen alternative
- if (last == 0)
- node = revisedPrev;
- else
- last->head = revisedPrev;
- }
- else if (next->tag == Node::Alt)
- {
- AltNode* nextList = (AltNode*)next;
- // Since we just had to dereference next to get here, we know that nextList
- // can't be nullptr in this case.
- AnalysisAssert(nextList != nullptr);
- // Append inner list to current list
- revisedPrev = UnionNodes(last == 0 ? node : last->head, nextList->head);
- if (revisedPrev != 0)
- {
- // Can merge head of list into previously seen alternative
- if (last ==0)
- node = revisedPrev;
- else
- last->head = revisedPrev;
- nextList = nextList->tail;
- }
- if (last == 0)
- node = Anew(ctAllocator, AltNode, node, nextList);
- else
- last->tail = nextList;
- AnalysisAssert(nextList != nullptr);
- while (nextList->tail != 0)
- nextList = nextList->tail;
- last = nextList;
- }
- else
- {
- // Append node
- AltNode* cons = Anew(ctAllocator, AltNode, next, 0);
- if (last == 0)
- node = Anew(ctAllocator, AltNode, node, cons);
- else
- last->tail = cons;
- last = cons;
- }
- }
- }
- template <typename P, const bool IsLiteral>
- inline bool Parser<P, IsLiteral>::IsEndOfAlternative()
- {
- EncodedChar ec = ECLookahead();
- // Could be terminating 0, but embedded 0 is part of alternative
- return (ec == 0 && IsEOF()) || ec == ')' || ec == '|' || (IsLiteral && ec == '/');
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::EnsureLitbuf(CharCount size)
- {
- if (litbufLen - litbufNext < size)
- {
- CharCount newLen = max(litbufLen, initLitbufSize);
- while (newLen < litbufNext + size)
- newLen *= 2;
- litbuf = (Char*)ctAllocator->Realloc(litbuf, litbufLen * sizeof(Char), newLen * sizeof(Char));
- litbufLen = newLen;
- }
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::AccumLiteral(MatchLiteralNode* deferredLiteralNode, Node* charOrLiteralNode)
- {
- Assert(charOrLiteralNode->tag == Node::MatchChar || charOrLiteralNode->tag == Node::MatchLiteral);
- CharCount addLen = charOrLiteralNode->LiteralLength();
- Assert(addLen > 0);
- if (deferredLiteralNode->length == 0)
- {
- // Start a new literal
- EnsureLitbuf(addLen);
- deferredLiteralNode->offset = litbufNext;
- deferredLiteralNode->length = addLen;
- charOrLiteralNode->AppendLiteral(litbufNext, litbufLen, litbuf);
- }
- else if (deferredLiteralNode->offset + deferredLiteralNode->length == litbufNext)
- {
- // Keep growing the current literal
- EnsureLitbuf(addLen);
- charOrLiteralNode->AppendLiteral(litbufNext, litbufLen, litbuf);
- deferredLiteralNode->length += addLen;
- }
- else if (charOrLiteralNode->tag == Node::MatchLiteral && deferredLiteralNode->offset + deferredLiteralNode->length == ((MatchLiteralNode*)charOrLiteralNode)->offset)
- {
- // Absorb next literal into current literal since they are adjacent
- deferredLiteralNode->length += addLen;
- }
- else
- {
- // Abandon current literal and start a fresh one (leaves gap)
- EnsureLitbuf(deferredLiteralNode->length + addLen);
- js_wmemcpy_s(litbuf + litbufNext, litbufLen - litbufNext, litbuf + deferredLiteralNode->offset, deferredLiteralNode->length);
- deferredLiteralNode->offset = litbufNext;
- litbufNext += deferredLiteralNode->length;
- charOrLiteralNode->AppendLiteral(litbufNext, litbufLen, litbuf);
- deferredLiteralNode->length += addLen;
- }
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::FinalTerm(Node* node, MatchLiteralNode* deferredLiteralNode)
- {
- if (node == deferredLiteralNode)
- {
- #if DBG
- if (deferredLiteralNode->length == 0)
- Assert(false);
- #endif
- Assert(deferredLiteralNode->offset < litbufNext);
- Assert(deferredLiteralNode->offset + deferredLiteralNode->length <= litbufNext);
- if (deferredLiteralNode->length == 1)
- {
- node = Anew(ctAllocator, MatchCharNode, litbuf[deferredLiteralNode->offset]);
- if (deferredLiteralNode->offset + deferredLiteralNode->length == litbufNext)
- // Reclaim last added character
- litbufNext--;
- // else: leave a gap in the literal buffer
- }
- else
- node = Anew(ctAllocator, MatchLiteralNode, *deferredLiteralNode);
- deferredLiteralNode->offset = 0;
- deferredLiteralNode->length = 0;
- }
- return node;
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::AlternativePass0(int depth)
- {
- while (!IsEndOfAlternative())
- TermPass0(depth);
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::AlternativePass1()
- {
- if (IsEndOfAlternative())
- return Anew(ctAllocator, SimpleNode, Node::Empty);
- MatchCharNode deferredCharNode(0);
- MatchLiteralNode deferredLiteralNode(0, 0);
- // Maintain the invariants:
- // - concat lists have two or more items
- // - concat list items are never concat lists
- // - a concat list never contains two consecutive match-character/match-literal nodes
- bool previousSurrogatePart = false;
- Node* node = TermPass1(&deferredCharNode, previousSurrogatePart);
- AnalysisAssert(node != nullptr);
- ConcatNode* last = 0;
- // First node may be a concat
- if (node->tag == Node::Concat)
- {
- last = (ConcatNode*)node;
- while (last->tail != 0)
- last = last->tail;
- }
- if (last == 0)
- {
- if (node->LiteralLength() > 0)
- {
- // Begin a new literal
- AccumLiteral(&deferredLiteralNode, node);
- node = &deferredLiteralNode;
- }
- }
- else
- {
- if (last->head->LiteralLength() > 0)
- {
- // Begin a new literal
- AccumLiteral(&deferredLiteralNode, last->head);
- last->head = &deferredLiteralNode;
- }
- }
- while (!IsEndOfAlternative())
- {
- Node* next = TermPass1(&deferredCharNode, previousSurrogatePart);
- AnalysisAssert(next != nullptr);
- if (next->LiteralLength() > 0)
- {
- // Begin a new literal or grow the existing literal
- AccumLiteral(&deferredLiteralNode, next);
- if (last == 0)
- {
- if (node != &deferredLiteralNode)
- {
- // So far we have first item and the current literal
- ConcatNode* cons = Anew(ctAllocator, ConcatNode, &deferredLiteralNode, 0);
- node = Anew(ctAllocator, ConcatNode, node, cons);
- last = cons;
- }
- // else: keep growing first literal
- }
- else
- {
- if (last->head != &deferredLiteralNode)
- {
- // Append a new literal node
- ConcatNode* cons = Anew(ctAllocator, ConcatNode, &deferredLiteralNode, 0);
- last->tail = cons;
- last = cons;
- }
- // else: keep growing current literal
- }
- }
- else if (next->tag == Node::Concat)
- {
- // Append this list to accumulated list
- ConcatNode* nextList = (ConcatNode*)next;
- if (nextList->head->LiteralLength() > 0 &&
- ((last == 0 && node == &deferredLiteralNode) ||
- (last != 0 && last->head == &deferredLiteralNode)))
- {
- // Absorb the next character or literal into the current literal
- // (may leave a gab in litbuf)
- AccumLiteral(&deferredLiteralNode, nextList->head);
- nextList = nextList->tail;
- // List has at least two items
- AnalysisAssert(nextList != 0);
- // List should be in canonical form, so no consecutive chars/literals
- Assert(nextList->head->LiteralLength() == 0);
- }
- if (last == 0)
- node = Anew(ctAllocator, ConcatNode, FinalTerm(node, &deferredLiteralNode), nextList);
- else
- {
- last->head = FinalTerm(last->head, &deferredLiteralNode);
- last->tail = nextList;
- }
- // NextList can't be nullptr, since it was last set as next, which
- // was dereferenced, or it was set on a path that already has this
- // analysis assert.
- AnalysisAssert(nextList != nullptr);
- while (nextList->tail != 0)
- nextList = nextList->tail;
- last = nextList;
- // No outstanding literals
- Assert(deferredLiteralNode.length == 0);
- // We just set this from nextList, which we know is not nullptr.
- AnalysisAssert(last != nullptr);
- if (last->head->LiteralLength() > 0)
- {
- // If the list ends with a literal, transfer it into deferredLiteralNode
- // so we can continue accumulating (won't leave a gab in litbuf)
- AccumLiteral(&deferredLiteralNode, last->head);
- // Can discard MatchLiteralNode since it lives in compile-time allocator
- last->head = &deferredLiteralNode;
- }
- }
- else
- {
- // Append this node to accumulated list
- ConcatNode* cons = Anew(ctAllocator, ConcatNode, next, 0);
- if (last == 0)
- node = Anew(ctAllocator, ConcatNode, FinalTerm(node, &deferredLiteralNode), cons);
- else
- {
- last->head = FinalTerm(last->head, &deferredLiteralNode);
- last->tail = cons;
- }
- last = cons;
- // No outstanding literals
- Assert(deferredLiteralNode.length == 0);
- }
- }
- if (last == 0)
- node = FinalTerm(node, &deferredLiteralNode);
- else
- last->head = FinalTerm(last->head, &deferredLiteralNode);
- // No outstanding literals
- Assert(deferredLiteralNode.length == 0);
- return node;
- }
- //
- // Terms
- //
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::NewLoopNode(CharCount lower, CharCountOrFlag upper, bool isGreedy, Node* body)
- {
- //
- // NOTE: We'd like to represent r? (i.e. r{0,1}) as r|<empty> since the loop representation has high overhead.
- // HOWEVER if r contains a group definition and could match empty then we must execute as a loop
- // so that group bindings are correctly reset on no progress (e.g.: /(a*)?/.exec("")). Thus we defer
- // this optimization until pass 4 of the optimizer, at which point we know whether r could match empty.
- //
- if (lower == 1 && upper == 1)
- return body;
- else if (lower == 0 && upper == 0)
- // Loop is equivalent to empty. If the loop body contains group definitions they will have already been
- // counted towards the overall number of groups. The matcher will initialize their contents to
- // undefined, and since the loop body would never execute the inner groups could never be updated from
- // undefined.
- return Anew(ctAllocator, SimpleNode, Node::Empty);
- else
- return Anew(ctAllocator, LoopNode, lower, upper, isGreedy, body);
- }
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::AtQuantifier()
- {
- // Could be terminating 0
- switch (ECLookahead())
- {
- case '*':
- case '+':
- case '?':
- return true;
- case '{':
- {
- CharCount lookahead = 1;
- while (ECCanConsume(lookahead + 1) && standardEncodedChars->IsDigit(ECLookahead(lookahead)))
- lookahead++;
- if (lookahead == 1 || !ECCanConsume(lookahead + 1))
- return false;
- switch (ECLookahead(lookahead))
- {
- case ',':
- lookahead++;
- if (ECCanConsume(lookahead + 1) && ECLookahead(lookahead) == '}')
- return true;
- else
- {
- CharCount saved = lookahead;
- while (ECCanConsume(lookahead + 1) && standardEncodedChars->IsDigit(ECLookahead(lookahead)))
- lookahead++;
- if (lookahead == saved)
- return false;
- return ECCanConsume(lookahead + 1) && ECLookahead(lookahead) == '}';
- }
- case '}':
- return true;
- default:
- return false;
- }
- }
- default:
- return false;
- }
- }
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::OptNonGreedy()
- {
- // Could be terminating 0
- if (ECLookahead() != '?')
- return true;
- ECConsume();
- return false;
- }
- template <typename P, const bool IsLiteral>
- CharCount Parser<P, IsLiteral>::RepeatCount()
- {
- CharCount n = 0;
- int digits = 0;
- while (true)
- {
- // Could be terminating 0
- EncodedChar ec = ECLookahead();
- if (!standardEncodedChars->IsDigit(ec))
- {
- if (digits == 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- return n;
- }
- if (n > MaxCharCount / 10)
- {
- break;
- }
- n *= 10;
- if (n > MaxCharCount - standardEncodedChars->DigitValue(ec))
- {
- break;
- }
- n += standardEncodedChars->DigitValue(ec);
- digits++;
- ECConsume();
- }
- Assert(digits != 0); // shouldn't be able to reach here with (digits == 0)
- // The token had a value larger than MaxCharCount so we didn't return the value and reached here instead.
- // Consume the rest of the token and return MaxCharCount.
- while (true)
- {
- EncodedChar ec = ECLookahead();
- if (!standardEncodedChars->IsDigit(ec))
- {
- return MaxCharCount;
- }
- ECConsume();
- }
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::TermPass0(int depth)
- {
- PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex);
- // Either we have a location at the start, or the end, never both. As in between it should have been cleared if surrogate pair
- // Or must be cleared if we didn't perform the check
- bool clearLocationIfPresent = this->tempLocationOfSurrogatePair != nullptr;
- switch (ECLookahead())
- {
- case '^':
- case '$':
- ECConsume();
- return;
- case '\\':
- ECConsume();
- if (AtomEscapePass0())
- return;
- break;
- case '(':
- // Can't combine into a single codeunit because of group present
- this->tempLocationOfSurrogatePair = nullptr;
- this->codePointAtTempLocation = 0;
- clearLocationIfPresent = false;
- ECConsume();
- switch (ECLookahead())
- {
- case '?':
- if (!ECCanConsume(2))
- Fail(JSERR_RegExpSyntax);
- switch (ECLookahead(1))
- {
- case '=':
- case '!':
- case ':':
- ECConsume(2);
- break;
- default:
- numGroups++;
- break;
- }
- break;
- default:
- numGroups++;
- break;
- }
- DisjunctionPass0(depth + 1);
- ECMust(')', JSERR_RegExpNoParen);
- break;
- case '.':
- ECConsume();
- break;
- case '[':
- ECConsume();
- this->tempLocationOfSurrogatePair = nullptr;
- this->codePointAtTempLocation = 0;
- this->tempLocationOfRange = next;
- CharacterClassPass0();
- this->tempLocationOfRange = nullptr;
- ECMust(']', JSERR_RegExpNoBracket);
- break;
- case ')':
- case '|':
- Fail(JSERR_RegExpSyntax);
- break;
- case ']':
- case '}':
- NextChar();
- break;
- case '*':
- case '+':
- case '?':
- case '{':
- if (AtQuantifier())
- Fail(JSERR_RegExpBadQuant);
- else
- ECConsume();
- break;
- case 0:
- if (IsEOF())
- // Terminating 0
- Fail(JSERR_RegExpSyntax);
- // else fall-through for embedded 0
- default:
- {
- const EncodedChar* current = next;
- Char c = NextChar();
- if (scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- TrackIfSurrogatePair(c, current, (uint32)(next - current));
- }
- // Closing '/' in literals should be caught explicitly
- Assert(!IsLiteral || c != '/');
- }
- break;
- }
- if (clearLocationIfPresent && this->tempLocationOfSurrogatePair != nullptr)
- {
- this->tempLocationOfSurrogatePair = nullptr;
- this->codePointAtTempLocation = 0;
- }
- if (AtQuantifier())
- {
- switch (ECLookahead())
- {
- case '*':
- case '+':
- case '?':
- ECConsume();
- OptNonGreedy();
- break;
- case '{':
- {
- ECConsume();
- CharCount lower = RepeatCount();
- switch (ECLookahead())
- {
- case ',':
- ECConsume();
- if (ECLookahead() == '}')
- {
- ECConsume();
- OptNonGreedy();
- }
- else
- {
- CharCount upper = RepeatCount();
- if (upper < lower)
- Fail(JSERR_RegExpSyntax);
- Assert(ECLookahead() == '}');
- ECConsume();
- OptNonGreedy();
- }
- break;
- case '}':
- ECConsume();
- OptNonGreedy();
- break;
- default:
- Assert(false);
- break;
- }
- break;
- }
- default:
- Assert(false);
- break;
- }
- }
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::TermPass1(MatchCharNode* deferredCharNode, bool& previousSurrogatePart)
- {
- PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex);
- Node* node = 0;
- bool containsSurrogatePair = false;
- switch (ECLookahead())
- {
- case '^':
- ECConsume();
- return Anew(ctAllocator, SimpleNode, Node::BOL); // No quantifier allowed
- case '$':
- ECConsume();
- return Anew(ctAllocator, SimpleNode, Node::EOL); // No quantifier allowed
- case '\\':
- ECConsume();
- if(SurrogatePairPass1(node, deferredCharNode, previousSurrogatePart))
- {
- break; // For quantifier
- }
- else if (AtomEscapePass1(node, deferredCharNode, previousSurrogatePart))
- {
- return node; // No quantifier allowed
- }
- break; // else: fall-through for opt quantifier
- case '(':
- ECConsume();
- switch (ECLookahead())
- {
- case '?':
- switch (ECLookahead(1))
- {
- case '=':
- ECConsume(2); // ?=
- node = DisjunctionPass1();
- Assert(ECLookahead() == ')');
- ECConsume(); // )
- node = Anew(ctAllocator, AssertionNode, false, node);
- break; // As per Annex B, allow this to be quantifiable
- case '!':
- ECConsume(2); // ?!
- node = DisjunctionPass1();
- Assert(ECLookahead() == ')');
- ECConsume(); // )
- node = Anew(ctAllocator, AssertionNode, true, node);
- break; // As per Annex B, allow this to be quantifiable
- case ':':
- ECConsume(2); // ?:
- node = DisjunctionPass1();
- Assert(ECLookahead() == ')');
- ECConsume(); // )
- break; // fall-through for opt quantifier
- default:
- {
- // ? not yet consumed
- int thisGroupId = nextGroupId++;
- node = DisjunctionPass1();
- Assert(ECLookahead() == ')');
- ECConsume(); // )
- node = Anew(ctAllocator, DefineGroupNode, thisGroupId, node);
- break; // fall-through for opt quantifier
- }
- }
- break;
- default:
- {
- // next char not yet consumed
- int thisGroupId = nextGroupId++;
- node = DisjunctionPass1();
- Assert(ECLookahead() == ')');
- ECConsume(); // )
- node = Anew(ctAllocator, DefineGroupNode, thisGroupId, node);
- break; // fall-through for opt quantifier
- }
- }
- break;
- case '.':
- {
- ECConsume();
- node = GetNodeWithValidCharacterSet('.');
- break; // fall-through for opt quantifier
- }
- case '[':
- ECConsume();
- if (unicodeFlagPresent)
- {
- containsSurrogatePair = this->currentSurrogatePairNode != nullptr && this->currentSurrogatePairNode->rangeLocation == next;
- }
- node = containsSurrogatePair ? CharacterClassPass1<true>() : CharacterClassPass1<false>();
- Assert(ECLookahead() == ']');
- ECConsume(); // ]
- break; // fall-through for opt quantifier
- #if DBG
- case ')':
- case '|':
- Assert(false);
- break;
- #endif
- case ']':
- // SPEC DEVIATION: This should be syntax error, instead accept as itself
- deferredCharNode->cs[0] = NextChar();
- node = deferredCharNode;
- break; // fall-through for opt quantifier
- #if DBG
- case '*':
- case '+':
- case '?':
- AssertMsg(false, "Allowed only in the escaped form. These should be caught by TermPass0.");
- break;
- #endif
- case 0:
- if (IsEOF())
- // Terminating 0
- Fail(JSERR_RegExpSyntax);
- // else fall-through for embedded 0
- default:
- if(SurrogatePairPass1(node, deferredCharNode, previousSurrogatePart))
- {
- break; //For quantifier
- }
- else
- {
- deferredCharNode->cs[0] = NextChar();
- node = deferredCharNode;
- break; // fall-through for opt quantifier
- }
- }
- Assert(node != 0);
- if (AtQuantifier())
- {
- switch (ECLookahead())
- {
- case '*':
- if (node == deferredCharNode)
- node = Anew(ctAllocator, MatchCharNode, *deferredCharNode);
- ECConsume();
- return NewLoopNode(0, CharCountFlag, OptNonGreedy(), node);
- case '+':
- if (node == deferredCharNode)
- node = Anew(ctAllocator, MatchCharNode, *deferredCharNode);
- ECConsume();
- return NewLoopNode(1, CharCountFlag, OptNonGreedy(), node);
- case '?':
- if (node == deferredCharNode)
- node = Anew(ctAllocator, MatchCharNode, *deferredCharNode);
- ECConsume();
- return NewLoopNode(0, 1, OptNonGreedy(), node);
- case '{':
- {
- if (node == deferredCharNode)
- node = Anew(ctAllocator, MatchCharNode, *deferredCharNode);
- ECConsume();
- CharCount lower = RepeatCount();
- switch (ECLookahead())
- {
- case ',':
- ECConsume();
- if (ECLookahead() == '}')
- {
- ECConsume();
- return NewLoopNode(lower, CharCountFlag, OptNonGreedy(), node);
- }
- else
- {
- CharCount upper = RepeatCount();
- Assert(lower <= upper);
- Assert(ECLookahead() == '}');
- ECConsume(); // }
- return NewLoopNode(lower, upper, OptNonGreedy(), node);
- }
- case '}':
- ECConsume();
- return NewLoopNode(lower, lower, OptNonGreedy(), node);
- default:
- Assert(false);
- break;
- }
- break;
- }
- default:
- Assert(false);
- break;
- }
- }
- return node;
- }
- #pragma warning(push)
- #pragma warning(disable:4702) // unreachable code
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::AtomEscapePass0()
- {
- EncodedChar ec = ECLookahead();
- if (ec == 0 && IsEOF())
- {
- // Terminating 0
- Fail(JSERR_RegExpSyntax);
- return false;
- }
- else if (standardEncodedChars->IsDigit(ec))
- {
- do
- {
- ECConsume();
- }
- while (standardEncodedChars->IsDigit(ECLookahead())); // terminating 0 is not a digit
- return false;
- }
- else if (ECLookahead() == 'c')
- {
- if (standardEncodedChars->IsLetter(ECLookahead(1))) // terminating 0 is not a letter
- {
- ECConsume(2);
- }
- else
- {
- DeferredFailIfUnicode(JSERR_RegExpInvalidEscape);
- }
- return false;
- }
- else
- {
- const EncodedChar *current = next;
- // An escaped '/' is ok
- Char c = NextChar();
- switch (c)
- {
- case 'b':
- case 'B':
- return true;
- // case 'c': handled as special case above
- case 'x':
- if (ECCanConsume(2) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)))
- ECConsume(2);
- break;
- case 'u':
- bool surrogateEncountered = false;
- int lengthOfSurrogate = TryParseExtendedUnicodeEscape(c, surrogateEncountered, true);
- if (lengthOfSurrogate > 0)
- {
- if (surrogateEncountered)
- {
- // If we don't have an allocator, we don't create nodes
- // Asserts in place as extra checks for when we do have an allocator
- Assert(this->ctAllocator == nullptr || this->currentSurrogatePairNode != nullptr);
- Assert(this->ctAllocator == nullptr || current == this->currentSurrogatePairNode->location);
- ECConsume(lengthOfSurrogate);
- }
- //Don't fall through
- break;
- }
- else if (ECCanConsume(4) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)) &&
- standardEncodedChars->IsHex(ECLookahead(2)) &&
- standardEncodedChars->IsHex(ECLookahead(3)))
- {
- if (this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- codepoint_t value = (standardEncodedChars->DigitValue(ECLookahead(0)) << 12) |
- (standardEncodedChars->DigitValue(ECLookahead(1)) << 8) |
- (standardEncodedChars->DigitValue(ECLookahead(2)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(3)));
- TrackIfSurrogatePair(value, (next - 1), 5);
- }
- ECConsume(4);
- }
- break;
- }
- // embedded 0 is ok
- return false;
- }
- }
- #pragma warning(pop)
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::AtomEscapePass1(Node*& node, MatchCharNode* deferredCharNode, bool& previousSurrogatePart)
- {
- Assert(!IsEOF()); // checked for terminating 0 in pass 0
- if (standardEncodedChars->IsDigit(ECLookahead()))
- {
- // As per Annex B, allow octal escapes as well as group references, disambiguate based on known
- // number of groups.
- if (ECLookahead() == '0')
- {
- // fall through for octal
- }
- else
- {
- // Could be a group reference, but only if between 1 and 5 digits which resolve to a valid group number
- int n = 0;
- CharCount digits = 0;
- do
- {
- n = n * 10 + (int)standardEncodedChars->DigitValue(ECLookahead(digits));
- digits++;
- }
- while (digits < 5 && ECCanConsume(digits + 1) && standardEncodedChars->IsDigit(ECLookahead(digits)));
- if (n >= numGroups ||
- (ECCanConsume(digits + 1) && standardEncodedChars->IsDigit(ECLookahead(digits))))
- {
- if (standardEncodedChars->IsOctal(ECLookahead()))
- {
- // fall through for octal
- }
- else
- {
- // \8 and \9 are identity escapes
- deferredCharNode->cs[0] = Chars<EncodedChar>::CTW(ECLookahead());
- ECConsume();
- node = deferredCharNode;
- return false; // not an assertion
- }
- }
- else
- {
- ECConsume(digits);
- node = Anew(ctAllocator, MatchGroupNode, n);
- return false; // not an assertion
- }
- }
- // Must be between 1 and 3 octal digits
- Assert(standardEncodedChars->IsOctal(ECLookahead())); // terminating 0 is not an octal
- uint n = 0;
- CharCount digits = 0;
- do
- {
- uint m = n * 8 + standardEncodedChars->DigitValue(ECLookahead());
- if (m > Chars<uint8>::MaxUChar) // Regex octal codes only support single byte (ASCII) characters.
- break;
- n = m;
- ECConsume();
- digits++;
- }
- while (digits < 3 && standardEncodedChars->IsOctal(ECLookahead())); // terminating 0 is not an octal
- deferredCharNode->cs[0] = UTC((UChar)n);
- node = deferredCharNode;
- return false; // not an assertion
- }
- else if (ECLookahead() == 'c')
- {
- Char c;
- if (standardEncodedChars->IsLetter(ECLookahead(1))) // terminating 0 is not a letter
- {
- c = UTC(Chars<EncodedChar>::CTU(ECLookahead(1)) % 32);
- ECConsume(2);
- }
- else
- {
- // SPEC DEVIATION: For non-letters or EOF, take the leading '\' to be itself, and
- // don't consume the 'c' or letter.
- c = '\\';
- }
- deferredCharNode->cs[0] = c;
- node = deferredCharNode;
- return false; // not an assertion
- }
- else
- {
- Char c = NextChar();
- switch (c)
- {
- case 'b':
- node = Anew(ctAllocator, WordBoundaryNode, false);
- return true; // Is an assertion
- case 'B':
- node = Anew(ctAllocator, WordBoundaryNode, true);
- return true; // Is an assertion
- case 'f':
- c = '\f';
- break; // fall-through for identity escape
- case 'n':
- c = '\n';
- break; // fall-through for identity escape
- case 'r':
- c = '\r';
- break; // fall-through for identity escape
- case 't':
- c = '\t';
- break; // fall-through for identity escape
- case 'v':
- c = '\v';
- break; // fall-through for identity escape
- case 'd':
- {
- MatchSetNode *setNode = Anew(ctAllocator, MatchSetNode, false, false);
- standardChars->SetDigits(ctAllocator, setNode->set);
- node = setNode;
- return false; // not an assertion
- }
- case 'D':
- {
- node = GetNodeWithValidCharacterSet('D');
- return false; // not an assertion
- }
- case 's':
- {
- MatchSetNode *setNode = Anew(ctAllocator, MatchSetNode, false, false);
- standardChars->SetWhitespace(ctAllocator, setNode->set);
- node = setNode;
- return false; // not an assertion
- }
- case 'S':
- {
- node = GetNodeWithValidCharacterSet('S');
- return false; // not an assertion
- }
- case 'w':
- {
- MatchSetNode *setNode = Anew(ctAllocator, MatchSetNode, false, false);
- if (this->unicodeFlagPresent && this->caseInsensitiveFlagPresent)
- {
- standardChars->SetWordIUChars(ctAllocator, setNode->set);
- }
- else
- {
- standardChars->SetWordChars(ctAllocator, setNode->set);
- }
- node = setNode;
- return false; // not an assertion
- }
- case 'W':
- {
- node = GetNodeWithValidCharacterSet('W');
- return false; // not an assertion
- }
- // case 'c': handled as special case above
- case 'x':
- if (ECCanConsume(2) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)))
- {
- c = UTC((standardEncodedChars->DigitValue(ECLookahead(0)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(1))));
- ECConsume(2);
- // fall-through for identity escape
- }
- // Take to be identity escape if ill-formed as per Annex B
- break;
- case 'u':
- if (unicodeFlagPresent && TryParseExtendedUnicodeEscape(c, previousSurrogatePart) > 0)
- break;
- else if (ECCanConsume(4) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)) &&
- standardEncodedChars->IsHex(ECLookahead(2)) &&
- standardEncodedChars->IsHex(ECLookahead(3)))
- {
- c = UTC((standardEncodedChars->DigitValue(ECLookahead(0)) << 12) |
- (standardEncodedChars->DigitValue(ECLookahead(1)) << 8) |
- (standardEncodedChars->DigitValue(ECLookahead(2)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(3))));
- ECConsume(4);
- // fall-through for identity escape
- }
- // Take to be identity escape if ill-formed as per Annex B
- break;
- case '^':
- case '$':
- case '\\':
- case '.':
- case '*':
- case '+':
- case '?':
- case '(':
- case ')':
- case '[':
- case ']':
- case '{':
- case '}':
- case '|':
- case '/':
- break; // fall-through for identity escape
- default:
- if (this->unicodeFlagPresent)
- {
- // As per #sec-forbidden-extensions, if unicode flag is present, we must disallow any other escape.
- this->Fail(JSERR_RegExpInvalidEscape); // throw SyntaxError
- }
- // As per Annex B, allow anything other than newlines and above. Embedded 0 is ok
- break;
- }
- // Must be an identity escape
- deferredCharNode->cs[0] = c;
- node = deferredCharNode;
- return false; // not an assertion
- }
- }
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::SurrogatePairPass1(Node*& node, MatchCharNode* deferredCharNode, bool& previousSurrogatePart)
- {
- if (!this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled() || !unicodeFlagPresent)
- {
- return false;
- }
- if (this->currentSurrogatePairNode != nullptr && this->currentSurrogatePairNode->location == this->next)
- {
- AssertMsg(!this->currentSurrogatePairNode->IsInsideRange(), "Should not be calling this pass if we are currently inside a range.");
- char16 lower, upper;
- uint tableIndex = 0, actualHigh = 0;
- codepoint_t equivClass[CaseInsensitive::EquivClassSize];
- if (caseInsensitiveFlagPresent && CaseInsensitive::RangeToEquivClass(tableIndex, this->currentSurrogatePairNode->value, this->currentSurrogatePairNode->value, actualHigh, equivClass))
- {
- Node *equivNode[CaseInsensitive::EquivClassSize];
- int indexForNextNode = 0;
- for (int i = 0; i < CaseInsensitive::EquivClassSize; i++)
- {
- bool alreadyAdded = false;
- for (int j = 0; j < i; j++)
- {
- if (equivClass[i] == equivClass[j])
- {
- alreadyAdded = true;
- break;
- }
- }
- if (!alreadyAdded)
- {
- if (Js::NumberUtilities::IsInSupplementaryPlane(equivClass[i]))
- {
- Js::NumberUtilities::CodePointAsSurrogatePair(equivClass[i], &lower, &upper);
- equivNode[indexForNextNode] = CreateSurrogatePairAtom(lower, upper);
- }
- else
- {
- equivNode[indexForNextNode] = Anew(ctAllocator, MatchCharNode, (Char)equivClass[i]);
- }
- indexForNextNode ++;
- }
- }
- Assert(indexForNextNode > 0);
- if (indexForNextNode == 1)
- {
- node = equivNode[0];
- }
- else
- {
- AltNode *altNode = Anew(ctAllocator, AltNode, equivNode[0], nullptr);
- AltNode *altNodeTail = altNode;
- for (int i = 1; i < indexForNextNode; i++)
- {
- altNodeTail->tail = Anew(ctAllocator, AltNode, equivNode[i], nullptr);
- altNodeTail = altNodeTail->tail;
- }
- node = altNode;
- }
- }
- else
- {
- Js::NumberUtilities::CodePointAsSurrogatePair(this->currentSurrogatePairNode->value, &lower, &upper);
- node = CreateSurrogatePairAtom(lower, upper);
- }
- previousSurrogatePart = false;
- Assert(ECCanConsume(this->currentSurrogatePairNode->length));
- ECConsumeMultiUnit(this->currentSurrogatePairNode->length);
- this->RestoreMultiUnits(this->currentSurrogatePairNode->multiUnits);
- this->currentSurrogatePairNode = this->currentSurrogatePairNode->next;
- return true;
- }
- return false;
- }
- //
- // Classes
- //
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::AtSecondSingletonClassAtom()
- {
- Assert(ECLookahead() == '-');
- if (ECLookahead(1) == '\\')
- {
- switch (ECLookahead(2))
- {
- case 'd':
- case 'D':
- case 's':
- case 'S':
- case 'w':
- case 'W':
- // These all denote non-singleton sets
- return false;
- default:
- // fall-through for singleton
- break;
- }
- }
- return true;
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::CharacterClassPass0()
- {
- // Could be terminating 0
- if (ECLookahead() == '^')
- ECConsume();
- EncodedChar nextChar = ECLookahead();
- const EncodedChar* current;
- codepoint_t lastCodepoint = INVALID_CODEPOINT;
- codepoint_t pendingRangeStart = INVALID_CODEPOINT;
- codepoint_t pendingRangeEnd = INVALID_CODEPOINT;
- bool previousSurrogatePart = false;
- while(nextChar != ']')
- {
- current = next;
- if (nextChar == '\0' && IsEOF())
- {
- // Report as unclosed '['
- Fail(JSERR_RegExpNoBracket);
- return;
- } // Otherwise embedded '\0' is ok
- else if (nextChar == '\\')
- {
- // Consume, as classescapepass0 expects for it to be consumed
- Char outChar = NextChar();
- // If previousSurrogatePart = true upon leaving this method, then we are going to pass through here twice
- // This is because \u{} escape sequence was encountered that is actually 2 characters, the second time we will pass consuming entire character
- if (ClassEscapePass0(outChar, previousSurrogatePart))
- {
- lastCodepoint = outChar;
- }
- else
- {
- // Last codepoint isn't a singleton, so no codepoint tracking for the sake of ranges is needed.
- lastCodepoint = INVALID_CODEPOINT;
- // Unless we have a possible range end, cancel our range tracking.
- if (pendingRangeEnd == INVALID_CODEPOINT)
- {
- pendingRangeStart = INVALID_CODEPOINT;
- }
- }
- }
- else if (nextChar == '-')
- {
- if (pendingRangeStart != INVALID_CODEPOINT || lastCodepoint == INVALID_CODEPOINT)
- {
- // '-' is the upper part of the range, with pendingRangeStart codepoint is the lower. Set lastCodePoint to '-' to check at the end of the while statement
- // OR
- // '-' is just a char, consume it and set as last char
- lastCodepoint = NextChar();
- }
- else
- {
- pendingRangeStart = this->next == this->positionAfterLastSurrogate
- ? this->valueOfLastSurrogate
- : lastCodepoint;
- lastCodepoint = INVALID_CODEPOINT;
- NextChar();
- }
- // If we have a pattern of the form [\ud800-\udfff], we need this to be interpreted as a range.
- // In order to achieve this, the two variables that we use to track surrogate pairs, namely
- // tempLocationOfSurrogatePair and previousSurrogatePart, need to be in a certain state.
- //
- // We need to reset tempLocationOfSurrogatePair as it points to the first Unicode escape (\ud800)
- // when we're here. We need to clear it in order not to have a surrogate pair when we process the
- // second escape (\udfff).
- //
- // previousSurrogatePart is used when we have a code point in the \u{...} extended format and the
- // character is in a supplementary plane. However, there is no need to change its value here. When
- // such an escape sequence is encountered, the first call to ClassEscapePass0() sets the variable
- // to true, but it rewinds the input back to the beginning of the escape sequence. The next
- // iteration of the loop here will again call ClassEscape0() with the same character and the
- // variable will this time be set to false. Therefore, the variable will always be false here.
- tempLocationOfSurrogatePair = nullptr;
- Assert(!previousSurrogatePart);
- }
- else
- {
- lastCodepoint = NextChar();
- if (scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- TrackIfSurrogatePair(lastCodepoint, current, (uint32)(next - current));
- }
- }
- if (!scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- // This is much easier to handle.
- if (pendingRangeStart != INVALID_CODEPOINT && lastCodepoint != INVALID_CODEPOINT)
- {
- Assert(pendingRangeStart < 0x10000);
- Assert(pendingRangeEnd == INVALID_CODEPOINT);
- if (pendingRangeStart > lastCodepoint)
- {
- Fail(JSERR_RegExpBadRange);
- }
- pendingRangeStart = lastCodepoint = INVALID_CODEPOINT;
- }
- }
- // We have a candidate for range end, and we have a range start.
- else if (pendingRangeStart != INVALID_CODEPOINT && (lastCodepoint != INVALID_CODEPOINT || pendingRangeEnd != INVALID_CODEPOINT))
- {
- // The following will be true at the end of each surrogate pair parse.
- // Note, the escape sequence \u{} is a two time parse, so this will be true on the second time around.
- if (this->next == this->positionAfterLastSurrogate)
- {
- lastCodepoint = this->valueOfLastSurrogate;
- Assert(!previousSurrogatePart);
- pendingRangeEnd = lastCodepoint;
- lastCodepoint = INVALID_CODEPOINT;
- }
- // If the next character is the end of range ']', then we can't have a surrogate pair.
- // The current character is the range end, if we don't already have a candidate.
- else if (ECLookahead() == ']' && pendingRangeEnd == INVALID_CODEPOINT)
- {
- pendingRangeEnd = lastCodepoint;
- }
- //If we get here, and pendingRangeEnd is set. Then one of the above has caused it to be set, or the previous iteration of the loop.
- if (pendingRangeEnd != INVALID_CODEPOINT)
- {
- char16 leftSingleChar, rightSingleChar, ignore;
- if (pendingRangeStart >= 0x10000)
- {
- Js::NumberUtilities::CodePointAsSurrogatePair(pendingRangeStart, &ignore, &leftSingleChar);
- }
- else
- {
- leftSingleChar = (char16)pendingRangeStart;
- }
- if (pendingRangeEnd >= 0x10000)
- {
- Js::NumberUtilities::CodePointAsSurrogatePair(pendingRangeEnd, &rightSingleChar, &ignore);
- }
- else
- {
- rightSingleChar = (char16)pendingRangeEnd;
- }
- // Here it is a bit tricky, we don't know if we have a unicode option specified.
- // If it is, then \ud800\udc00 - \ud800\udc01 is valid, otherwise invalid.
- if (pendingRangeStart < 0x10000 && pendingRangeEnd < 0x10000 && pendingRangeStart > pendingRangeEnd)
- {
- Fail(JSERR_RegExpBadRange);
- }
- else
- {
- if(leftSingleChar > rightSingleChar)
- {
- DeferredFailIfNotUnicode(JSERR_RegExpBadRange);
- }
- if (pendingRangeStart > pendingRangeEnd)
- {
- DeferredFailIfUnicode(JSERR_RegExpBadRange);
- }
- }
- pendingRangeStart = pendingRangeEnd = INVALID_CODEPOINT;
- }
- // The current char < 0x10000 is a candidate for the range end, but we need to iterate one more time.
- else
- {
- pendingRangeEnd = lastCodepoint;
- }
- }
- nextChar = ECLookahead();
- }
- // We should never have a pendingRangeEnd set when we exit the loop
- Assert(pendingRangeEnd == INVALID_CODEPOINT);
- }
- template <typename P, const bool IsLiteral>
- template <bool containsSurrogates>
- Node* Parser<P, IsLiteral>::CharacterClassPass1()
- {
- Assert(containsSurrogates ? unicodeFlagPresent : true);
- CharSet<codepoint_t> codePointSet;
- MatchSetNode deferredSetNode(false, false);
- MatchCharNode deferredCharNode(0);
- bool isNegation = false;
- if (ECLookahead() == '^')
- {
- isNegation = true;
- ECConsume();
- }
- // We aren't expecting any terminating null characters, only embedded ones that should treated as valid characters.
- // CharacterClassPass0 should have taken care of terminating null.
- codepoint_t pendingCodePoint = INVALID_CODEPOINT;
- codepoint_t pendingRangeStart = INVALID_CODEPOINT;
- EncodedChar nextChar = ECLookahead();
- bool previousWasASurrogate = false;
- bool currIsACharSet = false;
- bool prevWasACharSetAndPartOfRange = false;
- bool prevprevWasACharSetAndPartOfRange = false;
- while(nextChar != ']')
- {
- codepoint_t codePointToSet = INVALID_CODEPOINT;
- // Consume ahead of time if we have two backslashes, both cases below (previously Tracked surrogate pair, and ClassEscapePass1) assume it is.
- if (nextChar == '\\')
- {
- ECConsume();
- }
- // These if-blocks are the logical ClassAtomPass1, they weren't grouped into a method to simplify dealing with multiple out parameters.
- if (containsSurrogates && this->currentSurrogatePairNode != nullptr && this->currentSurrogatePairNode->location == this->next)
- {
- codePointToSet = pendingCodePoint;
- pendingCodePoint = this->currentSurrogatePairNode->value;
- Assert(ECCanConsume(this->currentSurrogatePairNode->length));
- ECConsumeMultiUnit(this->currentSurrogatePairNode->length);
- this->RestoreMultiUnits(this->currentSurrogatePairNode->multiUnits);
- this->currentSurrogatePairNode = this->currentSurrogatePairNode->next;
- }
- else if (nextChar == '\\')
- {
- Node* returnedNode = ClassEscapePass1(&deferredCharNode, &deferredSetNode, previousWasASurrogate);
- codePointToSet = pendingCodePoint;
- if (returnedNode->tag == Node::MatchSet)
- {
- if (pendingRangeStart != INVALID_CODEPOINT)
- {
- if (unicodeFlagPresent)
- {
- //A range containing a character class and the unicode flag is present, thus we end up having to throw a "Syntax" error here
- //This breaks the notion of Pass0 check for valid syntax, because during that time, the unicode flag is unknown.
- Fail(JSERR_UnicodeRegExpRangeContainsCharClass); //From #sec-patterns-static-semantics-early-errors-annexb
- }
- codePointSet.Set(ctAllocator, '-');
- }
- pendingCodePoint = INVALID_CODEPOINT;
- pendingRangeStart = INVALID_CODEPOINT;
- codePointSet.UnionInPlace(ctAllocator, deferredSetNode.set);
- currIsACharSet = true;
- }
- else
- {
- // Just a character
- pendingCodePoint = deferredCharNode.cs[0];
- }
- }
- else if (nextChar == '-')
- {
- if (pendingRangeStart != INVALID_CODEPOINT || pendingCodePoint == INVALID_CODEPOINT || ECLookahead(1) == ']')
- {
- // - is just a char, or end of a range.
- codePointToSet = pendingCodePoint;
- pendingCodePoint = '-';
- ECConsume();
- }
- else
- {
- pendingRangeStart = pendingCodePoint;
- ECConsume();
- }
- }
- else
- {
- // Just a character, consume it
- codePointToSet = pendingCodePoint;
- pendingCodePoint = NextChar();
- }
- if (codePointToSet != INVALID_CODEPOINT || prevprevWasACharSetAndPartOfRange)
- {
- if (prevprevWasACharSetAndPartOfRange)
- {
- //A range containing a character class and the unicode flag is present, thus we end up having to throw a "Syntax" error here
- //This breaks the notion of Pass0 check for valid syntax, because during that time, the unicode flag is unknown.
- if (unicodeFlagPresent)
- {
- Fail(JSERR_UnicodeRegExpRangeContainsCharClass);
- }
- if (pendingCodePoint != INVALID_CODEPOINT)
- {
- codePointSet.Set(ctAllocator, pendingCodePoint);
- }
- codePointSet.Set(ctAllocator, '-'); //Add '-' to set because a range was detected but turned out to be a union of character set with '-' and another atom.
- pendingRangeStart = pendingCodePoint = INVALID_CODEPOINT;
- }
- else if (pendingRangeStart != INVALID_CODEPOINT)
- {
- if (pendingRangeStart > pendingCodePoint)
- {
- //We have no unicodeFlag, but current range contains surrogates, thus we may end up having to throw a "Syntax" error here
- //This breaks the notion of Pass0 check for valid syntax, because we don't know if we have a unicode option
- Assert(!unicodeFlagPresent);
- Fail(JSERR_RegExpBadRange);
- }
-
- codePointSet.SetRange(ctAllocator, pendingRangeStart, pendingCodePoint);
- pendingRangeStart = pendingCodePoint = INVALID_CODEPOINT;
- }
- else
- {
- codePointSet.Set(ctAllocator, codePointToSet);
- }
- }
- nextChar = ECLookahead();
- prevprevWasACharSetAndPartOfRange = prevWasACharSetAndPartOfRange;
- prevWasACharSetAndPartOfRange = currIsACharSet && nextChar == '-';
- currIsACharSet = false;
- }
- if (pendingCodePoint != INVALID_CODEPOINT)
- {
- codePointSet.Set(ctAllocator, pendingCodePoint);
- }
- // At this point, we have a complete set of codepoints representing the range.
- // Before performing translation of any kind, we need to do some case filling.
- // At the point of this comment, there are no case mappings going cross-plane between simple
- // characters (< 0x10000) and supplementary characters (>= 0x10000)
- // However, it might still be the case, and this has to be handled.
- // On the other hand, we don't want to prevent optimizations that expect non-casefolded sets from happening.
- // At least for simple characters.
- // The simple case, is when the unicode flag isn't specified, we can go ahead and return the simple set.
- // Negations and case mappings will be handled later.
- if (!unicodeFlagPresent)
- {
- Assert(codePointSet.SimpleCharCount() == codePointSet.Count());
- MatchSetNode *simpleToReturn = Anew(ctAllocator, MatchSetNode, isNegation);
- codePointSet.CloneSimpleCharsTo(ctAllocator, simpleToReturn->set);
- return simpleToReturn;
- }
- // Everything past here must be under the flag
- Assert(scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled());
- if (codePointSet.IsEmpty())
- {
- return Anew(ctAllocator, MatchSetNode, false, false);
- }
- Node* prefixNode = nullptr;
- Node* suffixNode = nullptr;
- CharSet<codepoint_t> *toUseForTranslation = &codePointSet;
- // If a singleton, return a simple character
- bool isSingleton = !this->caseInsensitiveFlagPresent && !isNegation && codePointSet.IsSingleton();
- if (isSingleton)
- {
- codepoint_t singleton = codePointSet.Singleton();
- Node* toReturn = nullptr;
- if (singleton < 0x10000)
- {
- toReturn = Anew(ctAllocator, MatchCharNode, (char16)singleton);
- }
- else
- {
- Assert(unicodeFlagPresent);
- char16 lowerSurrogate, upperSurrogate;
- Js::NumberUtilities::CodePointAsSurrogatePair(singleton, &lowerSurrogate, &upperSurrogate);
- toReturn = CreateSurrogatePairAtom(lowerSurrogate, upperSurrogate);
- }
- codePointSet.Clear(ctAllocator);
- return toReturn;
- }
- // If negation, we want to complement the simple chars.
- // When a set is negated, optimizations skip checking if applicable, so we can go ahead and negate it here.
- CharSet<codepoint_t> negatedSet;
- if (!this->caseInsensitiveFlagPresent)
- {
- if (isNegation)
- {
- // Complement all characters, and use it as the set toTranslate
- codePointSet.ToComplement(ctAllocator, negatedSet);
- }
- toUseForTranslation = isNegation ? &negatedSet : &codePointSet;
- if (isNegation)
- {
- // Clear this, as we will no longer need this.
- codePointSet.FreeBody(ctAllocator);
- }
- }
- else
- {
- CharSet<codepoint_t> caseEquivalent;
- codePointSet.ToEquivClass(ctAllocator, caseEquivalent);
- // Equiv set can't have a reduced count of chars
- Assert(caseEquivalent.Count() >= codePointSet.Count());
- // Here we have a regex that has both case insensitive and unicode options.
- // The range might also be negated. If it is negated, we can go ahead and negate
- // the entire set as well as fill in cases, as optimizations wouldn't kick in anyways.
- if (isNegation)
- {
- codePointSet.Clear(ctAllocator);
- caseEquivalent.ToComplement(ctAllocator, codePointSet);
- caseEquivalent.FreeBody(ctAllocator);
- }
- else
- {
- codePointSet.CloneFrom(ctAllocator, caseEquivalent);
- }
- Assert(toUseForTranslation == &codePointSet);
- }
- uint totalCodePointsCount = toUseForTranslation->Count();
- uint simpleCharsCount = toUseForTranslation->SimpleCharCount();
- if (totalCodePointsCount == simpleCharsCount)
- {
- MatchSetNode *simpleToReturn = Anew(ctAllocator, MatchSetNode, isNegation);
- toUseForTranslation->CloneSimpleCharsTo(ctAllocator, simpleToReturn->set);
- return simpleToReturn;
- }
- if (simpleCharsCount > 0)
- {
- if (!toUseForTranslation->ContainSurrogateCodeUnits())
- {
- MatchSetNode *node = Anew(ctAllocator, MatchSetNode, false, false);
- toUseForTranslation->CloneSimpleCharsTo(ctAllocator, node->set);
- prefixNode = node;
- }
- else
- {
- MatchSetNode *node = Anew(ctAllocator, MatchSetNode, false, false);
- toUseForTranslation->CloneNonSurrogateCodeUnitsTo(ctAllocator, node->set);
- prefixNode = node;
- node = Anew(ctAllocator, MatchSetNode, false, false);
- toUseForTranslation->CloneSurrogateCodeUnitsTo(ctAllocator, node->set);
- suffixNode = node;
- }
- }
- Assert(unicodeFlagPresent);
- AltNode *headToReturn = prefixNode == nullptr ? nullptr : Anew(ctAllocator, AltNode, prefixNode, nullptr);
- AltNode *currentTail = headToReturn;
- codepoint_t charRangeSearchIndex = 0x10000, lowerCharOfRange = 0, upperCharOfRange = 0;
- while (toUseForTranslation->GetNextRange(charRangeSearchIndex, &lowerCharOfRange, &upperCharOfRange))
- {
- if (lowerCharOfRange == upperCharOfRange)
- {
- currentTail = this->AppendSurrogatePairToDisjunction(lowerCharOfRange, currentTail);
- }
- else
- {
- currentTail = this->AppendSurrogateRangeToDisjunction(lowerCharOfRange, upperCharOfRange, currentTail);
- }
- if (headToReturn == nullptr)
- {
- headToReturn = currentTail;
- }
- AnalysisAssert(currentTail != nullptr);
- while (currentTail->tail != nullptr)
- {
- currentTail = currentTail->tail;
- }
- charRangeSearchIndex = upperCharOfRange + 1;
- }
- if (suffixNode != nullptr)
- {
- currentTail->tail = Anew(ctAllocator, AltNode, suffixNode, nullptr);
- }
- toUseForTranslation->Clear(ctAllocator);
- if (headToReturn != nullptr && headToReturn->tail == nullptr)
- {
- return headToReturn->head;
- }
- return headToReturn;
- }
- #pragma warning(push)
- #pragma warning(disable:4702) // unreachable code
- template <typename P, const bool IsLiteral>
- bool Parser<P, IsLiteral>::ClassEscapePass0(Char& singleton, bool& previousSurrogatePart)
- {
- // Could be terminating 0
- EncodedChar ec = ECLookahead();
- if (ec == 0 && IsEOF())
- {
- Fail(JSERR_RegExpSyntax);
- return false;
- }
- else if (standardEncodedChars->IsOctal(ec))
- {
- uint n = 0;
- CharCount digits = 0;
- do
- {
- uint m = n * 8 + standardEncodedChars->DigitValue(ECLookahead());
- if (m > Chars<uint8>::MaxUChar) //Regex octal codes only support single byte (ASCII) characters.
- break;
- n = m;
- ECConsume();
- digits++;
- }
- while (digits < 3 && standardEncodedChars->IsOctal(ECLookahead())); // terminating 0 is not octal
- singleton = UTC((UChar)n);
- // Clear possible pair
- this->tempLocationOfSurrogatePair = nullptr;
- return true;
- }
- else
- {
- const EncodedChar* location = this->tempLocationOfSurrogatePair;
- // Clear it for now, otherwise to many branches to clear it on.
- this->tempLocationOfSurrogatePair = nullptr;
- // An escaped '/' is ok
- Char c = NextChar();
- switch (c)
- {
- case 'b':
- singleton = '\b';
- return true;
- case 'f':
- singleton = '\f';
- return true;
- case 'n':
- singleton = '\n';
- return true;
- case 'r':
- singleton = '\r';
- return true;
- case 't':
- singleton = '\t';
- return true;
- case 'v':
- singleton = '\v';
- return true;
- case 'd':
- case 'D':
- case 's':
- case 'S':
- case 'w':
- case 'W':
- return false;
- case 'c':
- if (standardEncodedChars->IsLetter(ECLookahead())) // terminating 0 is not a letter
- {
- singleton = UTC(Chars<EncodedChar>::CTU(ECLookahead()) % 32);
- ECConsume();
- }
- else
- {
- DeferredFailIfUnicode(JSERR_RegExpInvalidEscape); // Fail in unicode mode for non-letter escaped control characters according to 262 Annex-B RegExp grammar spec #prod-annexB-Term
- if (!IsEOF())
- {
- EncodedChar ecLookahead = ECLookahead();
- switch (ecLookahead)
- {
- case '-':
- case ']':
- singleton = c;
- break;
- default:
- singleton = UTC(Chars<EncodedChar>::CTU(ecLookahead) % 32);
- ECConsume();
- break;
- }
- }
- else
- singleton = c;
- }
- return true;
- case 'x':
- if (ECCanConsume(2) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)))
- {
- singleton = UTC((standardEncodedChars->DigitValue(ECLookahead(0)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(1))));
- ECConsume(2);
- }
- else
- singleton = c;
- return true;
- case 'u':
- this->tempLocationOfSurrogatePair = location;
- if (this->TryParseExtendedUnicodeEscape(singleton, previousSurrogatePart, true) > 0)
- return true;
- else if (ECCanConsume(4) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)) &&
- standardEncodedChars->IsHex(ECLookahead(2)) &&
- standardEncodedChars->IsHex(ECLookahead(3)))
- {
- singleton = UTC((standardEncodedChars->DigitValue(ECLookahead(0)) << 12) |
- (standardEncodedChars->DigitValue(ECLookahead(1)) << 8) |
- (standardEncodedChars->DigitValue(ECLookahead(2)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(3))));
- if (this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- // Current location
- TrackIfSurrogatePair(singleton, (next - 1), 5);
- }
- // The above if statement, if true, will clear tempLocationOfSurrogatePair if needs to.
- ECConsume(4);
- }
- else
- singleton = c;
- return true;
- default:
- // embedded 0 is ok
- singleton = c;
- return true;
- }
- }
- }
- #pragma warning(pop)
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::ClassEscapePass1(MatchCharNode* deferredCharNode, MatchSetNode* deferredSetNode, bool& previousSurrogatePart)
- {
- // Checked for terminating 0 is pass 0
- Assert(!IsEOF());
- if (standardEncodedChars->IsOctal(ECLookahead()))
- {
- // As per Annex B, allow octal escapes instead of just \0 (and \8 and \9 are identity escapes).
- // Must be between 1 and 3 octal digits.
- uint n = 0;
- CharCount digits = 0;
- do
- {
- uint m = n * 8 + standardEncodedChars->DigitValue(ECLookahead());
- if (m > Chars<uint8>::MaxUChar) //Regex octal codes only support single byte (ASCII) characters.
- break;
- n = m;
- ECConsume();
- digits++;
- }
- while (digits < 3 && standardEncodedChars->IsOctal(ECLookahead())); // terminating 0 is not octal
- deferredCharNode->cs[0] = UTC((UChar)n);
- return deferredCharNode;
- }
- else
- {
- Char c = NextChar();
- switch (c)
- {
- case 'b':
- c = '\b';
- break; // fall-through for identity escape
- case 'f':
- c = '\f';
- break; // fall-through for identity escape
- case 'n':
- c = '\n';
- break; // fall-through for identity escape
- case 'r':
- c = '\r';
- break; // fall-through for identity escape
- case 't':
- c = '\t';
- break; // fall-through for identity escape
- case 'v':
- c = '\v';
- break; // fall-through for identity escape
- case 'd':
- standardChars->SetDigits(ctAllocator, deferredSetNode->set);
- return deferredSetNode;
- case 'D':
- standardChars->SetNonDigits(ctAllocator, deferredSetNode->set);
- return deferredSetNode;
- case 's':
- standardChars->SetWhitespace(ctAllocator, deferredSetNode->set);
- return deferredSetNode;
- case 'S':
- standardChars->SetNonWhitespace(ctAllocator, deferredSetNode->set);
- return deferredSetNode;
- case 'w':
- standardChars->SetWordChars(ctAllocator, deferredSetNode->set);
- return deferredSetNode;
- case 'W':
- standardChars->SetNonWordChars(ctAllocator, deferredSetNode->set);
- return deferredSetNode;
- case 'c':
- if (standardEncodedChars->IsWord(ECLookahead())) // terminating 0 is not a word character
- {
- c = UTC(Chars<EncodedChar>::CTU(ECLookahead()) % 32);
- ECConsume();
- // fall-through for identity escape
- }
- else
- {
- // If the lookahead is a non-alphanumeric and not an underscore ('_'), then treat '\' and 'c' separately.
- //#sec-regular-expression-patterns-semantics
- ECRevert(1); //Put cursor back at 'c' and treat it as a non-escaped character.
- deferredCharNode->cs[0] = '\\';
- return deferredCharNode;
- }
- break;
- case 'x':
- if (ECCanConsume(2) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)))
- {
- c = UTC((standardEncodedChars->DigitValue(ECLookahead(0)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(1))));
- ECConsume(2);
- // fall-through for identity escape
- }
- // Take to be identity escape if ill-formed as per Annex B
- break;
- case 'u':
- if (unicodeFlagPresent && TryParseExtendedUnicodeEscape(c, previousSurrogatePart) > 0)
- break;
- else if (ECCanConsume(4) &&
- standardEncodedChars->IsHex(ECLookahead(0)) &&
- standardEncodedChars->IsHex(ECLookahead(1)) &&
- standardEncodedChars->IsHex(ECLookahead(2)) &&
- standardEncodedChars->IsHex(ECLookahead(3)))
- {
- c = UTC((standardEncodedChars->DigitValue(ECLookahead(0)) << 12) |
- (standardEncodedChars->DigitValue(ECLookahead(1)) << 8) |
- (standardEncodedChars->DigitValue(ECLookahead(2)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(3))));
- ECConsume(4);
- // fall-through for identity escape
- }
- // Take to be identity escape if ill-formed as per Annex B.
- break;
- default:
- // As per Annex B, allow anything other than newlines and above. Embedded 0 is ok.
- break;
- }
- // Must be an identity escape
- deferredCharNode->cs[0] = c;
- return deferredCharNode;
- }
- }
- //
- // Options
- //
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::Options(RegexFlags& flags)
- {
- while (true)
- {
- // Could be terminating 0
- EncodedChar ec = ECLookahead();
- CharCount consume;
- Char c;
- if (ec == 0)
- // Embedded 0 not valid
- return;
- else if (IsLiteral &&
- ec == '\\' &&
- ECCanConsume(6) &&
- ECLookahead(1) == 'u' &&
- standardEncodedChars->IsHex(ECLookahead(2)) &&
- standardEncodedChars->IsHex(ECLookahead(3)) &&
- standardEncodedChars->IsHex(ECLookahead(4)) &&
- standardEncodedChars->IsHex(ECLookahead(5)))
- {
- if (scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- Fail(JSERR_RegExpSyntax);
- return;
- }
- else
- {
- uint32 n = (standardEncodedChars->DigitValue(ECLookahead(2)) << 12) |
- (standardEncodedChars->DigitValue(ECLookahead(3)) << 8) |
- (standardEncodedChars->DigitValue(ECLookahead(4)) << 4) |
- (standardEncodedChars->DigitValue(ECLookahead(5)));
- c = UTC(n);
- consume = 6;
- }
- }
- else
- {
- c = Chars<EncodedChar>::CTW(ec);
- consume = 1;
- }
- switch (c) {
- case 'i':
- if ((flags & IgnoreCaseRegexFlag) != 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- flags = (RegexFlags)(flags | IgnoreCaseRegexFlag);
- break;
- case 'g':
- if ((flags & GlobalRegexFlag) != 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- flags = (RegexFlags)(flags | GlobalRegexFlag);
- break;
- case 'm':
- if ((flags & MultilineRegexFlag) != 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- flags = (RegexFlags)(flags | MultilineRegexFlag);
- break;
- case 's':
- if (scriptContext->GetConfig()->IsES2018RegExDotAllEnabled())
- {
- if ((flags & DotAllRegexFlag) != 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- flags = (RegexFlags)(flags | DotAllRegexFlag);
- break;
- }
- case 'u':
- // If we don't have unicode enabled, fall through to default
- if (scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled())
- {
- if ((flags & UnicodeRegexFlag) != 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- flags = (RegexFlags)(flags | UnicodeRegexFlag);
- // For telemetry
- CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, UnicodeRegexFlag, scriptContext);
- break;
- }
- case 'y':
- if (scriptContext->GetConfig()->IsES6RegExStickyEnabled())
- {
- if ((flags & StickyRegexFlag) != 0)
- {
- Fail(JSERR_RegExpSyntax);
- }
- flags = (RegexFlags)(flags | StickyRegexFlag);
- // For telemetry
- CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, StickyRegexFlag, scriptContext);
- break;
- }
- default:
- if (standardChars->IsWord(c))
- {
- // Outer context could never parse this character. Signal the syntax error as
- // being part of the regex.
- Fail(JSERR_RegExpSyntax);
- }
- return;
- }
- ECConsume(consume);
- }
- }
- //
- // Entry points
- //
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::ParseDynamic
- ( const EncodedChar* body
- , const EncodedChar* bodyLim
- , const EncodedChar* opts
- , const EncodedChar* optsLim
- , RegexFlags& flags )
- {
- Assert(!IsLiteral);
- Assert(body != 0);
- Assert(bodyLim >= body && *bodyLim == 0);
- Assert(opts == 0 || (optsLim >= opts && *optsLim == 0));
- // Body, pass 0
- SetPosition(body, bodyLim, true);
- PatternPass0();
- if (!IsEOF())
- Fail(JSERR_RegExpSyntax);
- // Options
- if (opts != 0)
- {
- SetPosition(opts, optsLim, false);
- Options(flags);
- if (!IsEOF())
- Fail(JSERR_RegExpSyntax);
- this->unicodeFlagPresent = (flags & UnifiedRegex::UnicodeRegexFlag) == UnifiedRegex::UnicodeRegexFlag;
- this->caseInsensitiveFlagPresent = (flags & UnifiedRegex::IgnoreCaseRegexFlag) == UnifiedRegex::IgnoreCaseRegexFlag;
- this->dotAllFlagPresent = (flags & UnifiedRegex::DotAllRegexFlag) == UnifiedRegex::DotAllRegexFlag;
- Assert(!this->unicodeFlagPresent || scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled());
- Assert(!this->dotAllFlagPresent || scriptContext->GetConfig()->IsES2018RegExDotAllEnabled());
- }
- else
- {
- this->unicodeFlagPresent = false;
- this->caseInsensitiveFlagPresent = false;
- this->dotAllFlagPresent = false;
- }
- // If this HR has been set, that means we have an earlier failure than the one caught above.
- if (this->deferredIfNotUnicodeError != nullptr && !this->unicodeFlagPresent)
- {
- throw ParseError(*deferredIfNotUnicodeError);
- }
- else if(this->deferredIfUnicodeError != nullptr && this->unicodeFlagPresent)
- {
- throw ParseError(*deferredIfUnicodeError);
- }
- this->currentSurrogatePairNode = this->surrogatePairList;
- // Body, pass 1
- SetPosition(body, bodyLim, true);
- Node* root = PatternPass1();
- Assert(IsEOF());
- return root;
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::ParseLiteral
- ( const EncodedChar* input
- , const EncodedChar* inputLim
- , CharCount& outBodyEncodedChars
- , CharCount& outTotalEncodedChars
- , CharCount& outBodyChars
- , CharCount& outTotalChars
- , RegexFlags& flags )
- {
- Assert(IsLiteral);
- Assert(input != 0);
- Assert(inputLim >= input); // *inputLim need not be 0 because of deferred parsing
- // To handle surrogate pairs properly under unicode option, we will collect information on location of the pairs
- // during pass 0, regardless if the option is present. (We aren't able to get it at that time)
- // During pass 1, we will use that information to correctly create appropriate nodes.
- // Body, pass 0
- SetPosition(input, inputLim, true);
- PatternPass0();
- outBodyEncodedChars = Chars<EncodedChar>::OSB(next, input);
- outBodyChars = Pos();
- // Options are needed for the next pass
- ECMust('/', ERRnoSlash);
- Options(flags);
- this->unicodeFlagPresent = (flags & UnifiedRegex::UnicodeRegexFlag) == UnifiedRegex::UnicodeRegexFlag;
- this->caseInsensitiveFlagPresent = (flags & UnifiedRegex::IgnoreCaseRegexFlag) == UnifiedRegex::IgnoreCaseRegexFlag;
- this->dotAllFlagPresent = (flags & UnifiedRegex::DotAllRegexFlag) == UnifiedRegex::DotAllRegexFlag;
- Assert(!this->unicodeFlagPresent || scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled());
- // If this HR has been set, that means we have an earlier failure than the one caught above.
- if (this->deferredIfNotUnicodeError != nullptr && !this->unicodeFlagPresent)
- {
- throw ParseError(*deferredIfNotUnicodeError);
- }
- else if(this->deferredIfUnicodeError != nullptr && this->unicodeFlagPresent)
- {
- throw ParseError(*deferredIfUnicodeError);
- }
- // Used below to proceed to the end of the regex
- const EncodedChar *pastOptions = next;
- this->currentSurrogatePairNode = this->surrogatePairList;
- // Body, pass 1
- SetPosition(input, inputLim, true);
- Node* root = PatternPass1();
- Assert(outBodyEncodedChars == Chars<EncodedChar>::OSB(next, input));
- Assert(outBodyChars == Pos());
- next = pastOptions;
- outTotalEncodedChars = Chars<EncodedChar>::OSB(next, input);
- outTotalChars = Pos();
- return root;
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::ParseLiteralNoAST
- ( const EncodedChar* input
- , const EncodedChar* inputLim
- , CharCount& outBodyEncodedChars
- , CharCount& outTotalEncodedChars
- , CharCount& outBodyChars
- , CharCount& outTotalChars )
- {
- Assert(IsLiteral);
- Assert(input != 0);
- Assert(inputLim >= input); // *inputLim need not be 0 because of deferred parsing
- // Body, pass 0
- SetPosition(input, inputLim, true);
- PatternPass0();
- outBodyEncodedChars = Chars<EncodedChar>::OSB(next, input);
- outBodyChars = Pos();
- // Options
- ECMust('/', ERRnoSlash);
- RegexFlags dummyFlags = NoRegexFlags;
- Options(dummyFlags);
- this->unicodeFlagPresent = (dummyFlags & UnifiedRegex::UnicodeRegexFlag) == UnifiedRegex::UnicodeRegexFlag;
- this->caseInsensitiveFlagPresent = (dummyFlags & UnifiedRegex::IgnoreCaseRegexFlag) == UnifiedRegex::IgnoreCaseRegexFlag;
- this->dotAllFlagPresent = (dummyFlags & UnifiedRegex::DotAllRegexFlag) == UnifiedRegex::DotAllRegexFlag;
- outTotalEncodedChars = Chars<EncodedChar>::OSB(next, input);
- outTotalChars = Pos();
- // If this HR has been set, that means we have an earlier failure than the one caught above.
- if (this->deferredIfNotUnicodeError != nullptr && !this->unicodeFlagPresent)
- {
- throw ParseError(*deferredIfNotUnicodeError);
- }
- else if(this->deferredIfUnicodeError != nullptr && this->unicodeFlagPresent)
- {
- throw ParseError(*deferredIfUnicodeError);
- }
- }
- template <typename P, const bool IsLiteral>
- template <const bool buildAST>
- RegexPattern * Parser<P, IsLiteral>::CompileProgram
- ( Node* root,
- const EncodedChar*& currentCharacter,
- const CharCount totalLen,
- const CharCount bodyChars,
- const CharCount bodyEncodedChars,
- const CharCount totalChars,
- const RegexFlags flags )
- {
- Assert(IsLiteral);
- Program* program = nullptr;
- if (buildAST)
- {
- const auto recycler = this->scriptContext->GetRecycler();
- program = Program::New(recycler, flags);
- this->CaptureSourceAndGroups(recycler, program, currentCharacter, bodyChars, bodyEncodedChars);
- }
- currentCharacter += totalLen;
- Assert(GetMultiUnits() == totalLen - totalChars);
- if (!buildAST)
- {
- return nullptr;
- }
- RegexPattern* pattern = RegexPattern::New(this->scriptContext, program, true);
- #if ENABLE_REGEX_CONFIG_OPTIONS
- RegexStats* stats = 0;
- if (REGEX_CONFIG_FLAG(RegexProfile))
- {
- stats = this->scriptContext->GetRegexStatsDatabase()->GetRegexStats(pattern);
- this->scriptContext->GetRegexStatsDatabase()->EndProfile(stats, RegexStats::Parse);
- }
- if (REGEX_CONFIG_FLAG(RegexTracing))
- {
- DebugWriter* tw = this->scriptContext->GetRegexDebugWriter();
- tw->Print(_u("// REGEX COMPILE "));
- pattern->Print(tw);
- tw->EOL();
- }
- if (REGEX_CONFIG_FLAG(RegexProfile))
- this->scriptContext->GetRegexStatsDatabase()->BeginProfile();
- #endif
- ArenaAllocator* rtAllocator = this->scriptContext->RegexAllocator();
- Compiler::Compile
- ( this->scriptContext
- , ctAllocator
- , rtAllocator
- , standardChars
- , program
- , root
- , this->GetLitbuf()
- , pattern
- #if ENABLE_REGEX_CONFIG_OPTIONS
- , w
- , stats
- #endif
- );
- #if ENABLE_REGEX_CONFIG_OPTIONS
- if (REGEX_CONFIG_FLAG(RegexProfile))
- this->scriptContext->GetRegexStatsDatabase()->EndProfile(stats, RegexStats::Compile);
- #endif
- #ifdef PROFILE_EXEC
- this->scriptContext->ProfileEnd(Js::RegexCompilePhase);
- #endif
- // CaptureSourceAndGroups throws if this condition doesn't hold.
- Assert(0 < pattern->NumGroups() && pattern->NumGroups() <= MAX_NUM_GROUPS);
- return pattern;
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::CaptureEmptySourceAndNoGroups(Program* program)
- {
- Assert(program->source == 0);
- program->source = const_cast<Char*>(_u(""));
- program->sourceLen = 0;
- program->numGroups = 1;
- // Remaining to set during compilation: litbuf, litbufLen, numLoops, insts, instsLen, entryPointLabel
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::CaptureSourceAndGroups(Recycler* recycler, Program* program, const EncodedChar* body, CharCount bodyChars, CharCount bodyEncodedChars)
- {
- Assert(program->source == 0);
- Assert(body != 0);
- // Program will own source string
- program->source = RecyclerNewArrayLeaf(recycler, Char, bodyChars + 1);
- // Don't need to zero out since we're writing to the buffer right here
- this->ConvertToUnicode(program->source, bodyChars, body, body + bodyEncodedChars);
- program->source[bodyChars] = 0;
- program->sourceLen = bodyChars;
- // We expect nextGroupId to be positive, because the full regexp itself always
- // counts as a capturing group.
- Assert(nextGroupId > 0);
- if (nextGroupId > MAX_NUM_GROUPS)
- {
- Js::JavascriptError::ThrowRangeError(this->scriptContext, JSERR_RegExpTooManyCapturingGroups);
- }
- else
- {
- program->numGroups = static_cast<uint16>(nextGroupId);
- }
- // Remaining to set during compilation: litbuf, litbufLen, numLoops, insts, instsLen, entryPointLabel
- }
- template <typename P, const bool IsLiteral>
- Node* Parser<P, IsLiteral>::GetNodeWithValidCharacterSet(EncodedChar cc)
- {
- Node* nodeToReturn = nullptr;
- if (this->scriptContext->GetConfig()->IsES6UnicodeExtensionsEnabled() && this->unicodeFlagPresent)
- {
- MatchSetNode *lowerRangeNode = Anew(ctAllocator, MatchSetNode, false, false);
- lowerRangeNode->set.SetRange(ctAllocator, (Char)0xD800, (Char)0xDBFF);
- MatchSetNode *upperRangeNode = Anew(ctAllocator, MatchSetNode, false, false);
- upperRangeNode->set.SetRange(ctAllocator, (Char)0xDC00, (Char)0xDFFF);
- ConcatNode* surrogateRangePairNode = Anew(ctAllocator, ConcatNode, lowerRangeNode, Anew(ctAllocator, ConcatNode, upperRangeNode, nullptr));
- // The MatchSet node will be split into [0-D7FFDC00-FFFF] (minus special characters like newline, whitespace, etc.) as a prefix, and a suffix of [D800-DBFF]
- // i.e. The MatchSet node can be with [0-D7FFDC00-FFFF] (minus special characters like newline, whitespace, etc.) OR [D800-DBFF]
- MatchSetNode* partialPrefixSetNode = Anew(ctAllocator, MatchSetNode, false, false);
- switch (cc)
- {
- case '.':
- if (this->dotAllFlagPresent)
- {
- standardChars->SetFullSet(ctAllocator, partialPrefixSetNode->set);
- }
- else
- {
- standardChars->SetNonNewline(ctAllocator, partialPrefixSetNode->set);
- }
- break;
- case 'S':
- standardChars->SetNonWhitespace(ctAllocator, partialPrefixSetNode->set);
- break;
- case 'D':
- standardChars->SetNonDigits(ctAllocator, partialPrefixSetNode->set);
- break;
- case 'W':
- if (this->caseInsensitiveFlagPresent)
- {
- standardChars->SetNonWordIUChars(ctAllocator, partialPrefixSetNode->set);
- }
- else
- {
- standardChars->SetNonWordChars(ctAllocator, partialPrefixSetNode->set);
- }
- break;
- default:
- AssertMsg(false, "");
- }
- partialPrefixSetNode->set.SubtractRange(ctAllocator, (Char)0xD800u, (Char)0xDBFFu);
- MatchSetNode* partialSuffixSetNode = Anew(ctAllocator, MatchSetNode, false, false);
- partialSuffixSetNode->set.SetRange(ctAllocator, (Char)0xD800u, (Char)0xDBFFu);
- AltNode* altNode = Anew(ctAllocator, AltNode, partialPrefixSetNode, Anew(ctAllocator, AltNode, surrogateRangePairNode, Anew(ctAllocator, AltNode, partialSuffixSetNode, nullptr)));
- nodeToReturn = altNode;
- }
- else
- {
- MatchSetNode* setNode = Anew(ctAllocator, MatchSetNode, false, false);
- switch (cc)
- {
- case '.':
- if (this->dotAllFlagPresent)
- {
- standardChars->SetFullSet(ctAllocator, setNode->set);
- }
- else
- {
- standardChars->SetNonNewline(ctAllocator, setNode->set);
- }
- break;
- case 'S':
- standardChars->SetNonWhitespace(ctAllocator, setNode->set);
- break;
- case 'D':
- standardChars->SetNonDigits(ctAllocator, setNode->set);
- break;
- case 'W':
- standardChars->SetNonWordChars(ctAllocator, setNode->set);
- break;
- default:
- AssertMsg(false, "");
- }
- nodeToReturn = setNode;
- }
- return nodeToReturn;
- }
- template <typename P, const bool IsLiteral>
- void Parser<P, IsLiteral>::FreeBody()
- {
- if (litbuf != 0)
- {
- ctAllocator->Free(litbuf, litbufLen);
- litbuf = nullptr;
- litbufLen = 0;
- litbufNext = 0;
- }
- }
- // Instantiate all templates
- #define INSTANTIATE_REGEX_PARSER_COMPILE(EncodingPolicy, IsLiteral, BuildAST) \
- template RegexPattern* Parser<EncodingPolicy, IsLiteral>::CompileProgram<BuildAST>(Node* root, const EncodedChar*& currentCharacter, const CharCount totalLen, const CharCount bodyChars, const CharCount bodyEncodedChars, const CharCount totalChars, const RegexFlags flags );
- #define INSTANTIATE_REGEX_PARSER(EncodingPolicy) \
- INSTANTIATE_REGEX_PARSER_COMPILE(EncodingPolicy, false, false) \
- INSTANTIATE_REGEX_PARSER_COMPILE(EncodingPolicy, false, true) \
- INSTANTIATE_REGEX_PARSER_COMPILE(EncodingPolicy, true, false) \
- INSTANTIATE_REGEX_PARSER_COMPILE(EncodingPolicy, true, true) \
- template class Parser<EncodingPolicy, false>; \
- template class Parser<EncodingPolicy, true>;
- // Instantiate the Parser
- INSTANTIATE_REGEX_PARSER(NullTerminatedUnicodeEncodingPolicy);
- INSTANTIATE_REGEX_PARSER(NotNullTerminatedUTF8EncodingPolicy);
- }
|