diff options
Diffstat (limited to 'Build/source/libs/icu/icu-xetex/i18n/regexcmp.cpp')
-rw-r--r-- | Build/source/libs/icu/icu-xetex/i18n/regexcmp.cpp | 3529 |
1 files changed, 3529 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-xetex/i18n/regexcmp.cpp b/Build/source/libs/icu/icu-xetex/i18n/regexcmp.cpp new file mode 100644 index 00000000000..558f80b6c48 --- /dev/null +++ b/Build/source/libs/icu/icu-xetex/i18n/regexcmp.cpp @@ -0,0 +1,3529 @@ + +// +// file: regexcmp.cpp +// +// Copyright (C) 2002-2007 International Business Machines Corporation and others. +// All Rights Reserved. +// +// This file contains the ICU regular expression compiler, which is responsible +// for processing a regular expression pattern into the compiled form that +// is used by the match finding engine. +// + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_REGULAR_EXPRESSIONS + +#include "unicode/unistr.h" +#include "unicode/uniset.h" +#include "unicode/uchar.h" +#include "unicode/uchriter.h" +#include "unicode/parsepos.h" +#include "unicode/parseerr.h" +#include "unicode/regex.h" +#include "util.h" +#include "cmemory.h" +#include "cstring.h" +#include "uvectr32.h" +#include "uassert.h" +#include "ucln_in.h" + +#include "regeximp.h" +#include "regexcst.h" // Contains state table for the regex pattern parser. + // generated by a Perl script. +#include "regexcmp.h" +#include "regexst.h" + + + +U_NAMESPACE_BEGIN + + + + + +//------------------------------------------------------------------------------ +// +// Constructor. +// +//------------------------------------------------------------------------------ +RegexCompile::RegexCompile(RegexPattern *rxp, UErrorCode &status) : fParenStack(status) +{ + fStatus = &status; + + fRXPat = rxp; + fScanIndex = 0; + fNextIndex = 0; + fPeekChar = -1; + fLineNum = 1; + fCharNum = 0; + fQuoteMode = FALSE; + fInBackslashQuote = FALSE; + fModeFlags = fRXPat->fFlags | 0x80000000; + fEOLComments = TRUE; + + fMatchOpenParen = -1; + fMatchCloseParen = -1; + fStringOpStart = -1; + + if (U_SUCCESS(status) && U_FAILURE(rxp->fDeferredStatus)) { + status = rxp->fDeferredStatus; + } +} + + + +//------------------------------------------------------------------------------ +// +// Destructor +// +//------------------------------------------------------------------------------ +RegexCompile::~RegexCompile() { +} + +//------------------------------------------------------------------------------ +// +// Compile regex pattern. The state machine for rexexp pattern parsing is here. +// The state tables are hand-written in the file regexcst.txt, +// and converted to the form used here by a perl +// script regexcst.pl +// +//------------------------------------------------------------------------------ +void RegexCompile::compile( + const UnicodeString &pat, // Source pat to be compiled. + UParseError &pp, // Error position info + UErrorCode &e) // Error Code +{ + fStatus = &e; + fParseErr = &pp; + fStackPtr = 0; + fStack[fStackPtr] = 0; + + if (U_FAILURE(*fStatus)) { + return; + } + + // There should be no pattern stuff in the RegexPattern object. They can not be reused. + U_ASSERT(fRXPat->fPattern.length() == 0); + + // Prepare the RegexPattern object to receive the compiled pattern. + // TODO: remove per-instance field, and just use globals directly. (But check perf) + fRXPat->fPattern = pat; + fRXPat->fStaticSets = RegexStaticSets::gStaticSets->fPropSets; + fRXPat->fStaticSets8 = RegexStaticSets::gStaticSets->fPropSets8; + + + // Initialize the pattern scanning state machine + fPatternLength = pat.length(); + uint16_t state = 1; + const RegexTableEl *tableEl; + nextChar(fC); // Fetch the first char from the pattern string. + + // + // Main loop for the regex pattern parsing state machine. + // Runs once per state transition. + // Each time through optionally performs, depending on the state table, + // - an advance to the the next pattern char + // - an action to be performed. + // - pushing or popping a state to/from the local state return stack. + // file regexcst.txt is the source for the state table. The logic behind + // recongizing the pattern syntax is there, not here. + // + for (;;) { + // Bail out if anything has gone wrong. + // Regex pattern parsing stops on the first error encountered. + if (U_FAILURE(*fStatus)) { + break; + } + + U_ASSERT(state != 0); + + // Find the state table element that matches the input char from the pattern, or the + // class of the input character. Start with the first table row for this + // state, then linearly scan forward until we find a row that matches the + // character. The last row for each state always matches all characters, so + // the search will stop there, if not before. + // + tableEl = &gRuleParseStateTable[state]; + REGEX_SCAN_DEBUG_PRINTF(("char, line, col = (\'%c\', %d, %d) state=%s ", + fC.fChar, fLineNum, fCharNum, RegexStateNames[state])); + + for (;;) { // loop through table rows belonging to this state, looking for one + // that matches the current input char. + REGEX_SCAN_DEBUG_PRINTF((".")); + if (tableEl->fCharClass < 127 && fC.fQuoted == FALSE && tableEl->fCharClass == fC.fChar) { + // Table row specified an individual character, not a set, and + // the input character is not quoted, and + // the input character matched it. + break; + } + if (tableEl->fCharClass == 255) { + // Table row specified default, match anything character class. + break; + } + if (tableEl->fCharClass == 254 && fC.fQuoted) { + // Table row specified "quoted" and the char was quoted. + break; + } + if (tableEl->fCharClass == 253 && fC.fChar == (UChar32)-1) { + // Table row specified eof and we hit eof on the input. + break; + } + + if (tableEl->fCharClass >= 128 && tableEl->fCharClass < 240 && // Table specs a char class && + fC.fQuoted == FALSE && // char is not escaped && + fC.fChar != (UChar32)-1) { // char is not EOF + UnicodeSet *uniset = RegexStaticSets::gStaticSets->fRuleSets[tableEl->fCharClass-128]; + if (uniset->contains(fC.fChar)) { + // Table row specified a character class, or set of characters, + // and the current char matches it. + break; + } + } + + // No match on this row, advance to the next row for this state, + tableEl++; + } + REGEX_SCAN_DEBUG_PRINTF(("\n")); + + // + // We've found the row of the state table that matches the current input + // character from the rules string. + // Perform any action specified by this row in the state table. + if (doParseActions(tableEl->fAction) == FALSE) { + // Break out of the state machine loop if the + // the action signalled some kind of error, or + // the action was to exit, occurs on normal end-of-rules-input. + break; + } + + if (tableEl->fPushState != 0) { + fStackPtr++; + if (fStackPtr >= kStackSize) { + error(U_REGEX_INTERNAL_ERROR); + REGEX_SCAN_DEBUG_PRINTF(("RegexCompile::parse() - state stack overflow.\n")); + fStackPtr--; + } + fStack[fStackPtr] = tableEl->fPushState; + } + + // + // NextChar. This is where characters are actually fetched from the pattern. + // Happens under control of the 'n' tag in the state table. + // + if (tableEl->fNextChar) { + nextChar(fC); + } + + // Get the next state from the table entry, or from the + // state stack if the next state was specified as "pop". + if (tableEl->fNextState != 255) { + state = tableEl->fNextState; + } else { + state = fStack[fStackPtr]; + fStackPtr--; + if (fStackPtr < 0) { + // state stack underflow + // This will occur if the user pattern has mis-matched parentheses, + // with extra close parens. + // + fStackPtr++; + error(U_REGEX_MISMATCHED_PAREN); + } + } + + } + + // + // The pattern has now been read and processed, and the compiled code generated. + // + + // Back-reference fixup + // + int32_t loc; + for (loc=0; loc<fRXPat->fCompiledPat->size(); loc++) { + int32_t op = fRXPat->fCompiledPat->elementAti(loc); + int32_t opType = URX_TYPE(op); + if (opType == URX_BACKREF || opType == URX_BACKREF_I) { + int32_t where = URX_VAL(op); + if (where > fRXPat->fGroupMap->size()) { + error(U_REGEX_INVALID_BACK_REF); + break; + } + where = fRXPat->fGroupMap->elementAti(where-1); + op = URX_BUILD(opType, where); + fRXPat->fCompiledPat->setElementAt(op, loc); + } + } + + + // + // Compute the number of digits requried for the largest capture group number. + // + fRXPat->fMaxCaptureDigits = 1; + int32_t n = 10; + for (;;) { + if (n > fRXPat->fGroupMap->size()) { + break; + } + fRXPat->fMaxCaptureDigits++; + n *= 10; + } + + // + // The pattern's fFrameSize so far has accumulated the requirements for + // storage for capture parentheses, counters, etc. that are encountered + // in the pattern. Add space for the two variables that are always + // present in the saved state: the input string position and the + // position in the compiled pattern. + // + fRXPat->fFrameSize+=2; + + // + // Get bounds for the minimum and maximum length of a string that this + // pattern can match. Used to avoid looking for matches in strings that + // are too short. + // + fRXPat->fMinMatchLen = minMatchLength(3, fRXPat->fCompiledPat->size()-1); + + // + // Optimization passes + // + matchStartType(); + OptDotStar(); + stripNOPs(); + + // + // Set up fast latin-1 range sets + // + int32_t numSets = fRXPat->fSets->size(); + fRXPat->fSets8 = new Regex8BitSet[numSets]; + int32_t i; + for (i=0; i<numSets; i++) { + UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(i); + fRXPat->fSets8[i].init(s); + } + +} + + + + + +//------------------------------------------------------------------------------ +// +// doParseAction Do some action during regex pattern parsing. +// Called by the parse state machine. +// +// Generation of the match engine PCode happens here, or +// in functions called from the parse actions defined here. +// +// +//------------------------------------------------------------------------------ +UBool RegexCompile::doParseActions(int32_t action) +{ + UBool returnVal = TRUE; + + switch ((Regex_PatternParseAction)action) { + + case doPatStart: + // Start of pattern compiles to: + //0 SAVE 2 Fall back to position of FAIL + //1 jmp 3 + //2 FAIL Stop if we ever reach here. + //3 NOP Dummy, so start of pattern looks the same as + // the start of an ( grouping. + //4 NOP Resreved, will be replaced by a save if there are + // OR | operators at the top level + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_STATE_SAVE, 2), *fStatus); + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_JMP, 3), *fStatus); + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_FAIL, 0), *fStatus); + + // Standard open nonCapture paren action emits the two NOPs and + // sets up the paren stack frame. + doParseActions(doOpenNonCaptureParen); + break; + + case doPatFinish: + // We've scanned to the end of the pattern + // The end of pattern compiles to: + // URX_END + // which will stop the runtime match engine. + // Encountering end of pattern also behaves like a close paren, + // and forces fixups of the State Save at the beginning of the compiled pattern + // and of any OR operations at the top level. + // + handleCloseParen(); + if (fParenStack.size() > 0) { + // Missing close paren in pattern. + error(U_REGEX_MISMATCHED_PAREN); + } + + // add the END operation to the compiled pattern. + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_END, 0), *fStatus); + + // Terminate the pattern compilation state machine. + returnVal = FALSE; + break; + + + + case doOrOperator: + // Scanning a '|', as in (A|B) + { + // Insert a SAVE operation at the start of the pattern section preceding + // this OR at this level. This SAVE will branch the match forward + // to the right hand side of the OR in the event that the left hand + // side fails to match and backtracks. Locate the position for the + // save from the location on the top of the parentheses stack. + int32_t savePosition = fParenStack.popi(); + int32_t op = fRXPat->fCompiledPat->elementAti(savePosition); + U_ASSERT(URX_TYPE(op) == URX_NOP); // original contents of reserved location + op = URX_BUILD(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+1); + fRXPat->fCompiledPat->setElementAt(op, savePosition); + + // Append an JMP operation into the compiled pattern. The operand for + // the JMP will eventually be the location following the ')' for the + // group. This will be patched in later, when the ')' is encountered. + op = URX_BUILD(URX_JMP, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Push the position of the newly added JMP op onto the parentheses stack. + // This registers if for fixup when this block's close paren is encountered. + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); + + // Append a NOP to the compiled pattern. This is the slot reserved + // for a SAVE in the event that there is yet another '|' following + // this one. + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); + } + break; + + + case doOpenCaptureParen: + // Open Paren. + // Compile to a + // - NOP, which later may be replaced by a save-state if the + // parenthesized group gets a * quantifier, followed by + // - START_CAPTURE n where n is stack frame offset to the capture group variables. + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + // + // Each capture group gets three slots in the save stack frame: + // 0: Capture Group start position (in input string being matched.) + // 1: Capture Group end positino. + // 2: Start of Match-in-progress. + // The first two locations are for a completed capture group, and are + // referred to by back references and the like. + // The third location stores the capture start position when an START_CAPTURE is + // encountered. This will be promoted to a completed capture when (and if) the corresponding + // END_CAPure is encountered. + { + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + int32_t varsLoc = fRXPat->fFrameSize; // Reserve three slots in match stack frame. + fRXPat->fFrameSize += 3; + int32_t cop = URX_BUILD(URX_START_CAPTURE, varsLoc); + fRXPat->fCompiledPat->addElement(cop, *fStatus); + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the two NOPs. Depending on what follows in the pattern, the + // NOPs may be changed to SAVE_STATE or JMP ops, with a target + // address of the end of the parenthesized group. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(capturing, *fStatus); // Frame type. + fParenStack.push(fRXPat->fCompiledPat->size()-3, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP loc + + // Save the mapping from group number to stack frame variable position. + fRXPat->fGroupMap->addElement(varsLoc, *fStatus); + } + break; + + case doOpenNonCaptureParen: + // Open non-caputuring (grouping only) Paren. + // Compile to a + // - NOP, which later may be replaced by a save-state if the + // parenthesized group gets a * quantifier, followed by + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + { + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the two NOPs. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(plain, *fStatus); // Begin a new frame. + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP loc + } + break; + + + case doOpenAtomicParen: + // Open Atomic Paren. (?> + // Compile to a + // - NOP, which later may be replaced if the parenthesized group + // has a quantifier, followed by + // - STO_SP save state stack position, so it can be restored at the ")" + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + { + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + int32_t varLoc = fRXPat->fDataSize; // Reserve a data location for saving the + fRXPat->fDataSize += 1; // state stack ptr. + int32_t stoOp = URX_BUILD(URX_STO_SP, varLoc); + fRXPat->fCompiledPat->addElement(stoOp, *fStatus); + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the two NOPs. Depending on what follows in the pattern, the + // NOPs may be changed to SAVE_STATE or JMP ops, with a target + // address of the end of the parenthesized group. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(atomic, *fStatus); // Frame type. + fParenStack.push(fRXPat->fCompiledPat->size()-3, *fStatus); // The first NOP + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP + } + break; + + + case doOpenLookAhead: + // Positive Look-ahead (?= stuff ) + // Compiles to + // 1 START_LA dataLoc + // 2. NOP reserved for use by quantifiers on the block. + // Look-ahead can't have quantifiers, but paren stack + // compile time conventions require the slot anyhow. + // 3. NOP may be replaced if there is are '|' ops in the block. + // 4. code for parenthesized stuff. + // 5. ENDLA + // + // Two data slots are reserved, for saving the stack ptr and the input position. + { + int32_t dataLoc = fRXPat->fDataSize; + fRXPat->fDataSize += 2; + int32_t op = URX_BUILD(URX_LA_START, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + op = URX_BUILD(URX_NOP, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the NOPs. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(lookAhead, *fStatus); // Frame type. + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP location + } + break; + + case doOpenLookAheadNeg: + // Negated Lookahead. (?! stuff ) + // Compiles to + // 1. START_LA dataloc + // 2. SAVE_STATE 7 // Fail within look-ahead block restores to this state, + // // which continues with the match. + // 3. NOP // Std. Open Paren sequence, for possible '|' + // 4. code for parenthesized stuff. + // 5. END_LA // Cut back stack, remove saved state from step 2. + // 6. FAIL // code in block succeeded, so neg. lookahead fails. + // 7. ... + { + int32_t dataLoc = fRXPat->fDataSize; + fRXPat->fDataSize += 2; + int32_t op = URX_BUILD(URX_LA_START, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + op = URX_BUILD(URX_STATE_SAVE, 0); // dest address will be patched later. + fRXPat->fCompiledPat->addElement(op, *fStatus); + + op = URX_BUILD(URX_NOP, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the StateSave and NOP. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push( negLookAhead, *fStatus); // Frame type + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The STATE_SAVE location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP location + + // Instructions #5 and #6 will be added when the ')' is encountered. + } + break; + + case doOpenLookBehind: + { + // Compile a (?<= look-behind open paren. + // + // Compiles to + // 0 URX_LB_START dataLoc + // 1 URX_LB_CONT dataLoc + // 2 MinMatchLen + // 3 MaxMatchLen + // 4 URX_NOP Standard '(' boilerplate. + // 5 URX_NOP Reserved slot for use with '|' ops within (block). + // 6 <code for LookBehind expression> + // 7 URX_LB_END dataLoc # Check match len, restore input len + // 8 URX_LA_END dataLoc # Restore stack, input pos + // + // Allocate a block of matcher data, to contain (when running a match) + // 0: Stack ptr on entry + // 1: Input Index on entry + // 2: Start index of match current match attempt. + // 3: Original Input String len. + + // Allocate data space + int32_t dataLoc = fRXPat->fDataSize; + fRXPat->fDataSize += 4; + + // Emit URX_LB_START + int32_t op = URX_BUILD(URX_LB_START, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Emit URX_LB_CONT + op = URX_BUILD(URX_LB_CONT, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + fRXPat->fCompiledPat->addElement(0, *fStatus); // MinMatchLength. To be filled later. + fRXPat->fCompiledPat->addElement(0, *fStatus); // MaxMatchLength. To be filled later. + + // Emit the NOP + op = URX_BUILD(URX_NOP, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the URX_LB_CONT and the NOP. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(lookBehind, *fStatus); // Frame type + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The 2nd NOP location + + // The final two instructions will be added when the ')' is encountered. + } + + break; + + case doOpenLookBehindNeg: + { + // Compile a (?<! negated look-behind open paren. + // + // Compiles to + // 0 URX_LB_START dataLoc # Save entry stack, input len + // 1 URX_LBN_CONT dataLoc # Iterate possible match positions + // 2 MinMatchLen + // 3 MaxMatchLen + // 4 continueLoc (9) + // 5 URX_NOP Standard '(' boilerplate. + // 6 URX_NOP Reserved slot for use with '|' ops within (block). + // 7 <code for LookBehind expression> + // 8 URX_LBN_END dataLoc # Check match len, cause a FAIL + // 9 ... + // + // Allocate a block of matcher data, to contain (when running a match) + // 0: Stack ptr on entry + // 1: Input Index on entry + // 2: Start index of match current match attempt. + // 3: Original Input String len. + + // Allocate data space + int32_t dataLoc = fRXPat->fDataSize; + fRXPat->fDataSize += 4; + + // Emit URX_LB_START + int32_t op = URX_BUILD(URX_LB_START, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Emit URX_LBN_CONT + op = URX_BUILD(URX_LBN_CONT, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + fRXPat->fCompiledPat->addElement(0, *fStatus); // MinMatchLength. To be filled later. + fRXPat->fCompiledPat->addElement(0, *fStatus); // MaxMatchLength. To be filled later. + fRXPat->fCompiledPat->addElement(0, *fStatus); // Continue Loc. To be filled later. + + // Emit the NOP + op = URX_BUILD(URX_NOP, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the URX_LB_CONT and the NOP. + fParenStack.push(fModeFlags, *fStatus); // Match mode state + fParenStack.push(lookBehindN, *fStatus); // Frame type + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP location + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The 2nd NOP location + + // The final two instructions will be added when the ')' is encountered. + } + break; + + case doConditionalExpr: + // Conditionals such as (?(1)a:b) + case doPerlInline: + // Perl inline-condtionals. (?{perl code}a|b) We're not perl, no way to do them. + error(U_REGEX_UNIMPLEMENTED); + break; + + + case doCloseParen: + handleCloseParen(); + if (fParenStack.size() <= 0) { + // Extra close paren, or missing open paren. + error(U_REGEX_MISMATCHED_PAREN); + } + break; + + case doNOP: + break; + + + case doBadOpenParenType: + case doRuleError: + error(U_REGEX_RULE_SYNTAX); + break; + + + case doMismatchedParenErr: + error(U_REGEX_MISMATCHED_PAREN); + break; + + case doPlus: + // Normal '+' compiles to + // 1. stuff to be repeated (already built) + // 2. jmp-sav 1 + // 3. ... + // + // Or, if the item to be repeated can match a zero length string, + // 1. STO_INP_LOC data-loc + // 2. body of stuff to be repeated + // 3. JMP_SAV_X 2 + // 4. ... + + // + // Or, if the item to be repeated is simple + // 1. Item to be repeated. + // 2. LOOP_SR_I set number (assuming repeated item is a set ref) + // 3. LOOP_C stack location + { + int32_t topLoc = blockTopLoc(FALSE); // location of item #1 + int32_t frameLoc; + + // Check for simple constructs, which may get special optimized code. + if (topLoc == fRXPat->fCompiledPat->size() - 1) { + int32_t repeatedOp = fRXPat->fCompiledPat->elementAti(topLoc); + + if (URX_TYPE(repeatedOp) == URX_SETREF) { + // Emit optimized code for [char set]+ + int32_t loopOpI = URX_BUILD(URX_LOOP_SR_I, URX_VAL(repeatedOp)); + fRXPat->fCompiledPat->addElement(loopOpI, *fStatus); + frameLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + int32_t loopOpC = URX_BUILD(URX_LOOP_C, frameLoc); + fRXPat->fCompiledPat->addElement(loopOpC, *fStatus); + break; + } + + if (URX_TYPE(repeatedOp) == URX_DOTANY || + URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { + // Emit Optimized code for .+ operations. + int32_t loopOpI = URX_BUILD(URX_LOOP_DOT_I, 0); + if (URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { + // URX_LOOP_DOT_I operand is a flag indicating . matches any mode. + loopOpI |= 1; + } + fRXPat->fCompiledPat->addElement(loopOpI, *fStatus); + frameLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + int32_t loopOpC = URX_BUILD(URX_LOOP_C, frameLoc); + fRXPat->fCompiledPat->addElement(loopOpC, *fStatus); + break; + } + + } + + // General case. + + // Check for minimum match length of zero, which requires + // extra loop-breaking code. + if (minMatchLength(topLoc, fRXPat->fCompiledPat->size()-1) == 0) { + // Zero length match is possible. + // Emit the code sequence that can handle it. + insertOp(topLoc); + frameLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + + int32_t op = URX_BUILD(URX_STO_INP_LOC, frameLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + op = URX_BUILD(URX_JMP_SAV_X, topLoc+1); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } else { + // Simpler code when the repeated body must match something non-empty + int32_t jmpOp = URX_BUILD(URX_JMP_SAV, topLoc); + fRXPat->fCompiledPat->addElement(jmpOp, *fStatus); + } + } + break; + + case doNGPlus: + // Non-greedy '+?' compiles to + // 1. stuff to be repeated (already built) + // 2. state-save 1 + // 3. ... + { + int32_t topLoc = blockTopLoc(FALSE); + int32_t saveStateOp = URX_BUILD(URX_STATE_SAVE, topLoc); + fRXPat->fCompiledPat->addElement(saveStateOp, *fStatus); + } + break; + + + case doOpt: + // Normal (greedy) ? quantifier. + // Compiles to + // 1. state save 3 + // 2. body of optional block + // 3. ... + // Insert the state save into the compiled pattern, and we're done. + { + int32_t saveStateLoc = blockTopLoc(TRUE); + int32_t saveStateOp = URX_BUILD(URX_STATE_SAVE, fRXPat->fCompiledPat->size()); + fRXPat->fCompiledPat->setElementAt(saveStateOp, saveStateLoc); + } + break; + + case doNGOpt: + // Non-greedy ?? quantifier + // compiles to + // 1. jmp 4 + // 2. body of optional block + // 3 jmp 5 + // 4. state save 2 + // 5 ... + // This code is less than ideal, with two jmps instead of one, because we can only + // insert one instruction at the top of the block being iterated. + { + int32_t jmp1_loc = blockTopLoc(TRUE); + int32_t jmp2_loc = fRXPat->fCompiledPat->size(); + + int32_t jmp1_op = URX_BUILD(URX_JMP, jmp2_loc+1); + fRXPat->fCompiledPat->setElementAt(jmp1_op, jmp1_loc); + + int32_t jmp2_op = URX_BUILD(URX_JMP, jmp2_loc+2); + fRXPat->fCompiledPat->addElement(jmp2_op, *fStatus); + + int32_t save_op = URX_BUILD(URX_STATE_SAVE, jmp1_loc+1); + fRXPat->fCompiledPat->addElement(save_op, *fStatus); + } + break; + + + case doStar: + // Normal (greedy) * quantifier. + // Compiles to + // 1. STATE_SAVE 4 + // 2. body of stuff being iterated over + // 3. JMP_SAV 2 + // 4. ... + // + // Or, if the body is a simple [Set], + // 1. LOOP_SR_I set number + // 2. LOOP_C stack location + // ... + // + // Or if this is a .* + // 1. LOOP_DOT_I (. matches all mode flag) + // 2. LOOP_C stack location + // + // Or, if the body can match a zero-length string, to inhibit infinite loops, + // 1. STATE_SAVE 5 + // 2. STO_INP_LOC data-loc + // 3. body of stuff + // 4. JMP_SAV_X 2 + // 5. ... + { + // location of item #1, the STATE_SAVE + int32_t topLoc = blockTopLoc(FALSE); + int32_t dataLoc = -1; + + // Check for simple *, where the construct being repeated + // compiled to single opcode, and might be optimizable. + if (topLoc == fRXPat->fCompiledPat->size() - 1) { + int32_t repeatedOp = fRXPat->fCompiledPat->elementAti(topLoc); + + if (URX_TYPE(repeatedOp) == URX_SETREF) { + // Emit optimized code for a [char set]* + int32_t loopOpI = URX_BUILD(URX_LOOP_SR_I, URX_VAL(repeatedOp)); + fRXPat->fCompiledPat->setElementAt(loopOpI, topLoc); + dataLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + int32_t loopOpC = URX_BUILD(URX_LOOP_C, dataLoc); + fRXPat->fCompiledPat->addElement(loopOpC, *fStatus); + break; + } + + if (URX_TYPE(repeatedOp) == URX_DOTANY || + URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { + // Emit Optimized code for .* operations. + int32_t loopOpI = URX_BUILD(URX_LOOP_DOT_I, 0); + if (URX_TYPE(repeatedOp) == URX_DOTANY_ALL) { + // URX_LOOP_DOT_I operand is a flag indicating . matches any mode. + loopOpI |= 1; + } + fRXPat->fCompiledPat->setElementAt(loopOpI, topLoc); + dataLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + int32_t loopOpC = URX_BUILD(URX_LOOP_C, dataLoc); + fRXPat->fCompiledPat->addElement(loopOpC, *fStatus); + break; + } + } + + // Emit general case code for this * + // The optimizations did not apply. + + int32_t saveStateLoc = blockTopLoc(TRUE); + int32_t jmpOp = URX_BUILD(URX_JMP_SAV, saveStateLoc+1); + + // Check for minimum match length of zero, which requires + // extra loop-breaking code. + if (minMatchLength(saveStateLoc, fRXPat->fCompiledPat->size()-1) == 0) { + insertOp(saveStateLoc); + dataLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + + int32_t op = URX_BUILD(URX_STO_INP_LOC, dataLoc); + fRXPat->fCompiledPat->setElementAt(op, saveStateLoc+1); + jmpOp = URX_BUILD(URX_JMP_SAV_X, saveStateLoc+2); + } + + // Locate the position in the compiled pattern where the match will continue + // after completing the *. (4 or 5 in the comment above) + int32_t continueLoc = fRXPat->fCompiledPat->size()+1; + + // Put together the save state op store it into the compiled code. + int32_t saveStateOp = URX_BUILD(URX_STATE_SAVE, continueLoc); + fRXPat->fCompiledPat->setElementAt(saveStateOp, saveStateLoc); + + // Append the URX_JMP_SAV or URX_JMPX operation to the compiled pattern. + fRXPat->fCompiledPat->addElement(jmpOp, *fStatus); + } + break; + + case doNGStar: + // Non-greedy *? quantifier + // compiles to + // 1. JMP 3 + // 2. body of stuff being iterated over + // 3. STATE_SAVE 2 + // 4 ... + { + int32_t jmpLoc = blockTopLoc(TRUE); // loc 1. + int32_t saveLoc = fRXPat->fCompiledPat->size(); // loc 3. + int32_t jmpOp = URX_BUILD(URX_JMP, saveLoc); + int32_t stateSaveOp = URX_BUILD(URX_STATE_SAVE, jmpLoc+1); + fRXPat->fCompiledPat->setElementAt(jmpOp, jmpLoc); + fRXPat->fCompiledPat->addElement(stateSaveOp, *fStatus); + } + break; + + + case doIntervalInit: + // The '{' opening an interval quantifier was just scanned. + // Init the counter varaiables that will accumulate the values as the digits + // are scanned. + fIntervalLow = 0; + fIntervalUpper = -1; + break; + + case doIntevalLowerDigit: + // Scanned a digit from the lower value of an {lower,upper} interval + { + int32_t digitValue = u_charDigitValue(fC.fChar); + U_ASSERT(digitValue >= 0); + fIntervalLow = fIntervalLow*10 + digitValue; + if (fIntervalLow < 0) { + error(U_REGEX_NUMBER_TOO_BIG); + } + } + break; + + case doIntervalUpperDigit: + // Scanned a digit from the upper value of an {lower,upper} interval + { + if (fIntervalUpper < 0) { + fIntervalUpper = 0; + } + int32_t digitValue = u_charDigitValue(fC.fChar); + U_ASSERT(digitValue >= 0); + fIntervalUpper = fIntervalUpper*10 + digitValue; + if (fIntervalUpper < 0) { + error(U_REGEX_NUMBER_TOO_BIG); + } + } + break; + + case doIntervalSame: + // Scanned a single value interval like {27}. Upper = Lower. + fIntervalUpper = fIntervalLow; + break; + + case doInterval: + // Finished scanning a normal {lower,upper} interval. Generate the code for it. + if (compileInlineInterval() == FALSE) { + compileInterval(URX_CTR_INIT, URX_CTR_LOOP); + } + break; + + case doPossessiveInterval: + // Finished scanning a Possessive {lower,upper}+ interval. Generate the code for it. + { + // Remember the loc for the top of the block being looped over. + // (Can not reserve a slot in the compiled pattern at this time, becuase + // compileInterval needs to reserve also, and blockTopLoc can only reserve + // once per block.) + int32_t topLoc = blockTopLoc(FALSE); + + // Produce normal looping code. + compileInterval(URX_CTR_INIT, URX_CTR_LOOP); + + // Surround the just-emitted normal looping code with a STO_SP ... LD_SP + // just as if the loop was inclosed in atomic parentheses. + + // First the STO_SP before the start of the loop + insertOp(topLoc); + int32_t varLoc = fRXPat->fDataSize; // Reserve a data location for saving the + fRXPat->fDataSize += 1; // state stack ptr. + int32_t op = URX_BUILD(URX_STO_SP, varLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + int32_t loopOp = fRXPat->fCompiledPat->popi(); + U_ASSERT(URX_TYPE(loopOp) == URX_CTR_LOOP && URX_VAL(loopOp) == topLoc); + loopOp++; // point LoopOp after the just-inserted STO_SP + fRXPat->fCompiledPat->push(loopOp, *fStatus); + + // Then the LD_SP after the end of the loop + op = URX_BUILD(URX_LD_SP, varLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + + break; + + case doNGInterval: + // Finished scanning a non-greedy {lower,upper}? interval. Generate the code for it. + compileInterval(URX_CTR_INIT_NG, URX_CTR_LOOP_NG); + break; + + case doIntervalError: + error(U_REGEX_BAD_INTERVAL); + break; + + case doLiteralChar: + // We've just scanned a "normal" character from the pattern, + literalChar(fC.fChar); + break; + + + + case doDotAny: + // scanned a ".", match any single character. + { + int32_t op; + if (fModeFlags & UREGEX_DOTALL) { + op = URX_BUILD(URX_DOTANY_ALL, 0); + } else { + op = URX_BUILD(URX_DOTANY, 0); + } + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + break; + + case doCaret: + { + int32_t op = (fModeFlags & UREGEX_MULTILINE)? URX_CARET_M : URX_CARET; + fRXPat->fCompiledPat->addElement(URX_BUILD(op, 0), *fStatus); + } + break; + + + case doDollar: + { + int32_t op = (fModeFlags & UREGEX_MULTILINE)? URX_DOLLAR_M : URX_DOLLAR; + fRXPat->fCompiledPat->addElement(URX_BUILD(op, 0), *fStatus); + } + break; + + case doBackslashA: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_CARET, 0), *fStatus); + break; + + case doBackslashB: + { + #if UCONFIG_NO_BREAK_ITERATION==1 + if (fModeFlags & UREGEX_UWORD) { + error(U_UNSUPPORTED_ERROR); + } + #endif + int32_t op = (fModeFlags & UREGEX_UWORD)? URX_BACKSLASH_BU : URX_BACKSLASH_B; + fRXPat->fCompiledPat->addElement(URX_BUILD(op, 1), *fStatus); + } + break; + + case doBackslashb: + { + #if UCONFIG_NO_BREAK_ITERATION==1 + if (fModeFlags & UREGEX_UWORD) { + error(U_UNSUPPORTED_ERROR); + } + #endif + int32_t op = (fModeFlags & UREGEX_UWORD)? URX_BACKSLASH_BU : URX_BACKSLASH_B; + fRXPat->fCompiledPat->addElement(URX_BUILD(op, 0), *fStatus); + } + break; + + case doBackslashD: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_BACKSLASH_D, 1), *fStatus); + break; + + case doBackslashd: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_BACKSLASH_D, 0), *fStatus); + break; + + case doBackslashG: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_BACKSLASH_G, 0), *fStatus); + break; + + case doBackslashS: + fRXPat->fCompiledPat->addElement( + URX_BUILD(URX_STAT_SETREF_N, URX_ISSPACE_SET), *fStatus); + break; + + case doBackslashs: + fRXPat->fCompiledPat->addElement( + URX_BUILD(URX_STATIC_SETREF, URX_ISSPACE_SET), *fStatus); + break; + + case doBackslashW: + fRXPat->fCompiledPat->addElement( + URX_BUILD(URX_STAT_SETREF_N, URX_ISWORD_SET), *fStatus); + break; + + case doBackslashw: + fRXPat->fCompiledPat->addElement( + URX_BUILD(URX_STATIC_SETREF, URX_ISWORD_SET), *fStatus); + break; + + case doBackslashX: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_BACKSLASH_X, 0), *fStatus); + break; + + + case doBackslashZ: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_DOLLAR, 0), *fStatus); + break; + + case doBackslashz: + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_BACKSLASH_Z, 0), *fStatus); + break; + + case doEscapeError: + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + break; + + case doExit: + returnVal = FALSE; + break; + + case doProperty: + { + UnicodeSet *theSet = scanProp(); + compileSet(theSet); + } + break; + + + case doScanUnicodeSet: + { + UnicodeSet *theSet = scanSet(); + compileSet(theSet); + } + break; + + case doEnterQuoteMode: + // Just scanned a \Q. Put character scanner into quote mode. + fQuoteMode = TRUE; + break; + + case doBackRef: + // BackReference. Somewhat unusual in that the front-end can not completely parse + // the regular expression, because the number of digits to be consumed + // depends on the number of capture groups that have been defined. So + // we have to do it here instead. + { + int32_t numCaptureGroups = fRXPat->fGroupMap->size(); + int32_t groupNum = 0; + UChar32 c = fC.fChar; + + for (;;) { + // Loop once per digit, for max allowed number of digits in a back reference. + int32_t digit = u_charDigitValue(c); + groupNum = groupNum * 10 + digit; + if (groupNum >= numCaptureGroups) { + break; + } + c = peekCharLL(); + if (RegexStaticSets::gStaticSets->fRuleDigitsAlias->contains(c) == FALSE) { + break; + } + nextCharLL(); + } + + // Scan of the back reference in the source regexp is complete. Now generate + // the compiled code for it. + // Because capture groups can be forward-referenced by back-references, + // we fill the operand with the capture group number. At the end + // of compilation, it will be changed to the variable's location. + U_ASSERT(groupNum > 0); + int32_t op; + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + op = URX_BUILD(URX_BACKREF_I, groupNum); + } else { + op = URX_BUILD(URX_BACKREF, groupNum); + } + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + break; + + + case doPossessivePlus: + // Possessive ++ quantifier. + // Compiles to + // 1. STO_SP + // 2. body of stuff being iterated over + // 3. STATE_SAVE 5 + // 4. JMP 2 + // 5. LD_SP + // 6. ... + // + // Note: TODO: This is pretty inefficient. A mass of saved state is built up + // then unconditionally discarded. Perhaps introduce a new opcode + // + { + // Emit the STO_SP + int32_t topLoc = blockTopLoc(TRUE); + int32_t stoLoc = fRXPat->fDataSize; + fRXPat->fDataSize++; // Reserve the data location for storing save stack ptr. + int32_t op = URX_BUILD(URX_STO_SP, stoLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + // Emit the STATE_SAVE + op = URX_BUILD(URX_STATE_SAVE, fRXPat->fCompiledPat->size()+2); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Emit the JMP + op = URX_BUILD(URX_JMP, topLoc+1); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Emit the LD_SP + op = URX_BUILD(URX_LD_SP, stoLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + break; + + case doPossessiveStar: + // Possessive *+ quantifier. + // Compiles to + // 1. STO_SP loc + // 2. STATE_SAVE 5 + // 3. body of stuff being iterated over + // 4. JMP 2 + // 5. LD_SP loc + // 6 ... + // TODO: do something to cut back the state stack each time through the loop. + { + // Reserve two slots at the top of the block. + int32_t topLoc = blockTopLoc(TRUE); + insertOp(topLoc); + + // emit STO_SP loc + int32_t stoLoc = fRXPat->fDataSize; + fRXPat->fDataSize++; // Reserve the data location for storing save stack ptr. + int32_t op = URX_BUILD(URX_STO_SP, stoLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + // Emit the SAVE_STATE 5 + int32_t L7 = fRXPat->fCompiledPat->size()+1; + op = URX_BUILD(URX_STATE_SAVE, L7); + fRXPat->fCompiledPat->setElementAt(op, topLoc+1); + + // Append the JMP operation. + op = URX_BUILD(URX_JMP, topLoc+1); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Emit the LD_SP loc + op = URX_BUILD(URX_LD_SP, stoLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + break; + + case doPossessiveOpt: + // Possessive ?+ quantifier. + // Compiles to + // 1. STO_SP loc + // 2. SAVE_STATE 5 + // 3. body of optional block + // 4. LD_SP loc + // 5. ... + // + { + // Reserve two slots at the top of the block. + int32_t topLoc = blockTopLoc(TRUE); + insertOp(topLoc); + + // Emit the STO_SP + int32_t stoLoc = fRXPat->fDataSize; + fRXPat->fDataSize++; // Reserve the data location for storing save stack ptr. + int32_t op = URX_BUILD(URX_STO_SP, stoLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc); + + // Emit the SAVE_STATE + int32_t continueLoc = fRXPat->fCompiledPat->size()+1; + op = URX_BUILD(URX_STATE_SAVE, continueLoc); + fRXPat->fCompiledPat->setElementAt(op, topLoc+1); + + // Emit the LD_SP + op = URX_BUILD(URX_LD_SP, stoLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + break; + + + case doBeginMatchMode: + fNewModeFlags = fModeFlags; + fSetModeFlag = TRUE; + break; + + case doMatchMode: // (?i) and similar + { + int32_t bit = 0; + switch (fC.fChar) { + case 0x69: /* 'i' */ bit = UREGEX_CASE_INSENSITIVE; break; + case 0x6d: /* 'm' */ bit = UREGEX_MULTILINE; break; + case 0x73: /* 's' */ bit = UREGEX_DOTALL; break; + case 0x77: /* 'w' */ bit = UREGEX_UWORD; break; + case 0x78: /* 'x' */ bit = UREGEX_COMMENTS; break; + case 0x2d: /* '-' */ fSetModeFlag = FALSE; break; + default: + U_ASSERT(FALSE); // Should never happen. Other chars are filtered out + // by the scanner. + } + if (fSetModeFlag) { + fNewModeFlags |= bit; + } else { + fNewModeFlags &= ~bit; + } + } + break; + + case doSetMatchMode: + // We've got a (?i) or similar. The match mode is being changed, but + // the change is not scoped to a parenthesized block. + U_ASSERT(fNewModeFlags < 0); + fModeFlags = fNewModeFlags; + + // Prevent any string from spanning across the change of match mode. + // Otherwise the pattern "abc(?i)def" would make a single string of "abcdef" + fixLiterals(); + break; + + + case doMatchModeParen: + // We've got a (?i: or similar. Begin a parenthesized block, save old + // mode flags so they can be restored at the close of the block. + // + // Compile to a + // - NOP, which later may be replaced by a save-state if the + // parenthesized group gets a * quantifier, followed by + // - NOP, which may later be replaced by a save-state if there + // is an '|' alternation within the parens. + { + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_NOP, 0), *fStatus); + + // On the Parentheses stack, start a new frame and add the postions + // of the two NOPs (a normal non-capturing () frame, except for the + // saving of the orignal mode flags.) + fParenStack.push(fModeFlags, *fStatus); + fParenStack.push(flags, *fStatus); // Frame Marker + fParenStack.push(fRXPat->fCompiledPat->size()-2, *fStatus); // The first NOP + fParenStack.push(fRXPat->fCompiledPat->size()-1, *fStatus); // The second NOP + + // Set the current mode flags to the new values. + U_ASSERT(fNewModeFlags < 0); + fModeFlags = fNewModeFlags; + } + break; + + case doBadModeFlag: + error(U_REGEX_INVALID_FLAG); + break; + + case doSuppressComments: + // We have just scanned a '(?'. We now need to prevent the character scanner from + // treating a '#' as a to-the-end-of-line comment. + // (This Perl compatibility just gets uglier and uglier to do...) + fEOLComments = FALSE; + break; + + + + default: + U_ASSERT(FALSE); + error(U_REGEX_INTERNAL_ERROR); + break; + } + + if (U_FAILURE(*fStatus)) { + returnVal = FALSE; + } + + return returnVal; +} + + + +//------------------------------------------------------------------------------ +// +// literalChar We've encountered a literal character from the pattern, +// or an escape sequence that reduces to a character. +// Add it to the string containing all literal chars/strings from +// the pattern. +// If we are in a pattern string already, add the new char to it. +// If we aren't in a pattern string, begin one now. +// +//------------------------------------------------------------------------------ +void RegexCompile::literalChar(UChar32 c) { + int32_t op; // An operation in the compiled pattern. + int32_t opType; + int32_t patternLoc; // A position in the compiled pattern. + int32_t stringLen; + + + // If the last thing compiled into the pattern was not a literal char, + // force this new literal char to begin a new string, and not append to the previous. + op = fRXPat->fCompiledPat->lastElementi(); + opType = URX_TYPE(op); + if (!(opType == URX_STRING_LEN || opType == URX_ONECHAR || opType == URX_ONECHAR_I)) { + fixLiterals(); + } + + if (fStringOpStart == -1) { + // First char of a string in the pattern. + // Emit a OneChar op into the compiled pattern. + emitONE_CHAR(c); + + // Also add it to the string pool, in case we get a second adjacent literal + // and want to change form ONE_CHAR to STRING + fStringOpStart = fRXPat->fLiteralText.length(); + fRXPat->fLiteralText.append(c); + return; + } + + // We are adding onto an existing string + fRXPat->fLiteralText.append(c); + + op = fRXPat->fCompiledPat->lastElementi(); + opType = URX_TYPE(op); + U_ASSERT(opType == URX_ONECHAR || opType == URX_ONECHAR_I || opType == URX_STRING_LEN); + + // If the most recently emitted op is a URX_ONECHAR, + if (opType == URX_ONECHAR || opType == URX_ONECHAR_I) { + if (U16_IS_TRAIL(c) && U16_IS_LEAD(URX_VAL(op))) { + // The most recently emitted op is a ONECHAR that was the first half + // of a surrogate pair. Update the ONECHAR's operand to be the + // supplementary code point resulting from both halves of the pair. + c = U16_GET_SUPPLEMENTARY(URX_VAL(op), c); + op = URX_BUILD(opType, c); + patternLoc = fRXPat->fCompiledPat->size() - 1; + fRXPat->fCompiledPat->setElementAt(op, patternLoc); + return; + } + + // The most recently emitted op is a ONECHAR. + // We've now received another adjacent char. Change the ONECHAR op + // to a string op. + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + op = URX_BUILD(URX_STRING_I, fStringOpStart); + } else { + op = URX_BUILD(URX_STRING, fStringOpStart); + } + patternLoc = fRXPat->fCompiledPat->size() - 1; + fRXPat->fCompiledPat->setElementAt(op, patternLoc); + op = URX_BUILD(URX_STRING_LEN, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + + // The pattern contains a URX_SRING / URX_STRING_LEN. Update the + // string length to reflect the new char we just added to the string. + stringLen = fRXPat->fLiteralText.length() - fStringOpStart; + op = URX_BUILD(URX_STRING_LEN, stringLen); + patternLoc = fRXPat->fCompiledPat->size() - 1; + fRXPat->fCompiledPat->setElementAt(op, patternLoc); +} + + + +//------------------------------------------------------------------------------ +// +// emitONE_CHAR emit a ONE_CHAR op into the generated code. +// Choose cased or uncased version, depending on the +// match mode and whether the character itself is cased. +// +//------------------------------------------------------------------------------ +void RegexCompile::emitONE_CHAR(UChar32 c) { + int32_t op; + if ((fModeFlags & UREGEX_CASE_INSENSITIVE) && + u_hasBinaryProperty(c, UCHAR_CASE_SENSITIVE)) { + // We have a cased character, and are in case insensitive matching mode. + c = u_foldCase(c, U_FOLD_CASE_DEFAULT); + op = URX_BUILD(URX_ONECHAR_I, c); + } else { + // Uncased char, or case sensitive match mode. + // Either way, just generate a literal compare of the char. + op = URX_BUILD(URX_ONECHAR, c); + } + fRXPat->fCompiledPat->addElement(op, *fStatus); +} + + +//------------------------------------------------------------------------------ +// +// fixLiterals When compiling something that can follow a literal +// string in a pattern, we need to "fix" any preceding +// string, which will cause any subsequent literals to +// begin a new string, rather than appending to the +// old one. +// +// Optionally, split the last char of the string off into +// a single "ONE_CHAR" operation, so that quantifiers can +// apply to that char alone. Example: abc* +// The * must apply to the 'c' only. +// +//------------------------------------------------------------------------------ +void RegexCompile::fixLiterals(UBool split) { + int32_t stringStart = fStringOpStart; // start index of the current literal string + int32_t op; // An op from/for the compiled pattern. + int32_t opType; // An opcode type from the compiled pattern. + int32_t stringLastCharIdx; + UChar32 lastChar; + int32_t stringNextToLastCharIdx; + UChar32 nextToLastChar; + int32_t stringLen; + + fStringOpStart = -1; + if (!split) { + return; + } + + // Split: We need to ensure that the last item in the compiled pattern does + // not refer to a literal string of more than one char. If it does, + // separate the last char from the rest of the string. + + // If the last operation from the compiled pattern is not a string, + // nothing needs to be done + op = fRXPat->fCompiledPat->lastElementi(); + opType = URX_TYPE(op); + if (opType != URX_STRING_LEN) { + return; + } + stringLen = URX_VAL(op); + + // + // Find the position of the last code point in the string (might be a surrogate pair) + // + stringLastCharIdx = fRXPat->fLiteralText.length(); + stringLastCharIdx = fRXPat->fLiteralText.moveIndex32(stringLastCharIdx, -1); + lastChar = fRXPat->fLiteralText.char32At(stringLastCharIdx); + + // The string should always be at least two code points long, meaning that there + // should be something before the last char position that we just found. + U_ASSERT(stringLastCharIdx > stringStart); + stringNextToLastCharIdx = fRXPat->fLiteralText.moveIndex32(stringLastCharIdx, -1); + U_ASSERT(stringNextToLastCharIdx >= stringStart); + nextToLastChar = fRXPat->fLiteralText.char32At(stringNextToLastCharIdx); + + if (stringNextToLastCharIdx > stringStart) { + // The length of string remaining after removing one char is two or more. + // Leave the string in the compiled pattern, shorten it by one char, + // and append a URX_ONECHAR op for the last char. + stringLen -= (fRXPat->fLiteralText.length() - stringLastCharIdx); + op = URX_BUILD(URX_STRING_LEN, stringLen); + fRXPat->fCompiledPat->setElementAt(op, fRXPat->fCompiledPat->size() -1); + emitONE_CHAR(lastChar); + } else { + // The original string consisted of exactly two characters. Replace + // the existing compiled URX_STRING/URX_STRING_LEN ops with a pair + // of URX_ONECHARs. + fRXPat->fCompiledPat->setSize(fRXPat->fCompiledPat->size() -2); + emitONE_CHAR(nextToLastChar); + emitONE_CHAR(lastChar); + } +} + + + + + + +//------------------------------------------------------------------------------ +// +// insertOp() Insert a slot for a new opcode into the already +// compiled pattern code. +// +// Fill the slot with a NOP. Our caller will replace it +// with what they really wanted. +// +//------------------------------------------------------------------------------ +void RegexCompile::insertOp(int32_t where) { + UVector32 *code = fRXPat->fCompiledPat; + U_ASSERT(where>0 && where < code->size()); + + int32_t nop = URX_BUILD(URX_NOP, 0); + code->insertElementAt(nop, where, *fStatus); + + // Walk through the pattern, looking for any ops with targets that + // were moved down by the insert. Fix them. + int32_t loc; + for (loc=0; loc<code->size(); loc++) { + int32_t op = code->elementAti(loc); + int32_t opType = URX_TYPE(op); + int32_t opValue = URX_VAL(op); + if ((opType == URX_JMP || + opType == URX_JMPX || + opType == URX_STATE_SAVE || + opType == URX_CTR_LOOP || + opType == URX_CTR_LOOP_NG || + opType == URX_JMP_SAV || + opType == URX_RELOC_OPRND) && opValue > where) { + // Target location for this opcode is after the insertion point and + // needs to be incremented to adjust for the insertion. + opValue++; + op = URX_BUILD(opType, opValue); + code->setElementAt(op, loc); + } + } + + // Now fix up the parentheses stack. All positive values in it are locations in + // the compiled pattern. (Negative values are frame boundaries, and don't need fixing.) + for (loc=0; loc<fParenStack.size(); loc++) { + int32_t x = fParenStack.elementAti(loc); + U_ASSERT(x < code->size()); + if (x>where) { + x++; + fParenStack.setElementAt(x, loc); + } + } + + if (fMatchCloseParen > where) { + fMatchCloseParen++; + } + if (fMatchOpenParen > where) { + fMatchOpenParen++; + } +} + + + +//------------------------------------------------------------------------------ +// +// blockTopLoc() Find or create a location in the compiled pattern +// at the start of the operation or block that has +// just been compiled. Needed when a quantifier (* or +// whatever) appears, and we need to add an operation +// at the start of the thing being quantified. +// +// (Parenthesized Blocks) have a slot with a NOP that +// is reserved for this purpose. .* or similar don't +// and a slot needs to be added. +// +// parameter reserveLoc : TRUE - ensure that there is space to add an opcode +// at the returned location. +// FALSE - just return the address, +// do not reserve a location there. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::blockTopLoc(UBool reserveLoc) { + int32_t theLoc; + if (fRXPat->fCompiledPat->size() == fMatchCloseParen) + { + // The item just processed is a parenthesized block. + theLoc = fMatchOpenParen; // A slot is already reserved for us. + U_ASSERT(theLoc > 0); + U_ASSERT(URX_TYPE(((uint32_t)fRXPat->fCompiledPat->elementAti(theLoc))) == URX_NOP); + } + else { + // Item just compiled is a single thing, a ".", or a single char, or a set reference. + // No slot for STATE_SAVE was pre-reserved in the compiled code. + // We need to make space now. + fixLiterals(TRUE); // If last item was a string, separate the last char. + theLoc = fRXPat->fCompiledPat->size()-1; + if (reserveLoc) { + /*int32_t opAtTheLoc = fRXPat->fCompiledPat->elementAti(theLoc);*/ + int32_t nop = URX_BUILD(URX_NOP, 0); + fRXPat->fCompiledPat->insertElementAt(nop, theLoc, *fStatus); + } + } + return theLoc; +} + + + +//------------------------------------------------------------------------------ +// +// handleCloseParen When compiling a close paren, we need to go back +// and fix up any JMP or SAVE operations within the +// parenthesized block that need to target the end +// of the block. The locations of these are kept on +// the paretheses stack. +// +// This function is called both when encountering a +// real ) and at the end of the pattern. +// +//------------------------------------------------------------------------------ +void RegexCompile::handleCloseParen() { + int32_t patIdx; + int32_t patOp; + if (fParenStack.size() <= 0) { + error(U_REGEX_MISMATCHED_PAREN); + return; + } + + // Force any literal chars that may follow the close paren to start a new string, + // and not attach to any preceding it. + fixLiterals(FALSE); + + // Fixup any operations within the just-closed parenthesized group + // that need to reference the end of the (block). + // (The first one popped from the stack is an unused slot for + // alternation (OR) state save, but applying the fixup to it does no harm.) + for (;;) { + patIdx = fParenStack.popi(); + if (patIdx < 0) { + // value < 0 flags the start of the frame on the paren stack. + break; + } + U_ASSERT(patIdx>0 && patIdx <= fRXPat->fCompiledPat->size()); + patOp = fRXPat->fCompiledPat->elementAti(patIdx); + U_ASSERT(URX_VAL(patOp) == 0); // Branch target for JMP should not be set. + patOp |= fRXPat->fCompiledPat->size(); // Set it now. + fRXPat->fCompiledPat->setElementAt(patOp, patIdx); + fMatchOpenParen = patIdx; + } + + // At the close of any parenthesized block, restore the match mode flags to + // the value they had at the open paren. Saved value is + // at the top of the paren stack. + fModeFlags = fParenStack.popi(); + U_ASSERT(fModeFlags < 0); + + // DO any additional fixups, depending on the specific kind of + // parentesized grouping this is + + switch (patIdx) { + case plain: + case flags: + // No additional fixups required. + // (Grouping-only parentheses) + break; + case capturing: + // Capturing Parentheses. + // Insert a End Capture op into the pattern. + // The frame offset of the variables for this cg is obtained from the + // start capture op and put it into the end-capture op. + { + int32_t captureOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen+1); + U_ASSERT(URX_TYPE(captureOp) == URX_START_CAPTURE); + + int32_t frameVarLocation = URX_VAL(captureOp); + int32_t endCaptureOp = URX_BUILD(URX_END_CAPTURE, frameVarLocation); + fRXPat->fCompiledPat->addElement(endCaptureOp, *fStatus); + } + break; + case atomic: + // Atomic Parenthesis. + // Insert a LD_SP operation to restore the state stack to the position + // it was when the atomic parens were entered. + { + int32_t stoOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen+1); + U_ASSERT(URX_TYPE(stoOp) == URX_STO_SP); + int32_t stoLoc = URX_VAL(stoOp); + int32_t ldOp = URX_BUILD(URX_LD_SP, stoLoc); + fRXPat->fCompiledPat->addElement(ldOp, *fStatus); + } + break; + + case lookAhead: + { + int32_t startOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen-1); + U_ASSERT(URX_TYPE(startOp) == URX_LA_START); + int32_t dataLoc = URX_VAL(startOp); + int32_t op = URX_BUILD(URX_LA_END, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + break; + + case negLookAhead: + { + // See comment at doOpenLookAheadNeg + int32_t startOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen-1); + U_ASSERT(URX_TYPE(startOp) == URX_LA_START); + int32_t dataLoc = URX_VAL(startOp); + int32_t op = URX_BUILD(URX_LA_END, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + op = URX_BUILD(URX_FAIL, 0); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Patch the URX_SAVE near the top of the block. + int32_t saveOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen); + U_ASSERT(URX_TYPE(saveOp) == URX_STATE_SAVE); + int32_t dest = fRXPat->fCompiledPat->size(); + saveOp = URX_BUILD(URX_STATE_SAVE, dest); + fRXPat->fCompiledPat->setElementAt(saveOp, fMatchOpenParen); + } + break; + + case lookBehind: + { + // See comment at doOpenLookBehind. + + // Append the URX_LB_END and URX_LA_END to the compiled pattern. + int32_t startOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen-4); + U_ASSERT(URX_TYPE(startOp) == URX_LB_START); + int32_t dataLoc = URX_VAL(startOp); + int32_t op = URX_BUILD(URX_LB_END, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + op = URX_BUILD(URX_LA_END, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Determine the min and max bounds for the length of the + // string that the pattern can match. + // An unbounded upper limit is an error. + int32_t patEnd = fRXPat->fCompiledPat->size() - 1; + int32_t minML = minMatchLength(fMatchOpenParen, patEnd); + int32_t maxML = maxMatchLength(fMatchOpenParen, patEnd); + if (maxML == INT32_MAX) { + error(U_REGEX_LOOK_BEHIND_LIMIT); + break; + } + U_ASSERT(minML <= maxML); + + // Insert the min and max match len bounds into the URX_LB_CONT op that + // appears at the top of the look-behind block, at location fMatchOpenParen+1 + fRXPat->fCompiledPat->setElementAt(minML, fMatchOpenParen-2); + fRXPat->fCompiledPat->setElementAt(maxML, fMatchOpenParen-1); + + } + break; + + + + case lookBehindN: + { + // See comment at doOpenLookBehindNeg. + + // Append the URX_LBN_END to the compiled pattern. + int32_t startOp = fRXPat->fCompiledPat->elementAti(fMatchOpenParen-5); + U_ASSERT(URX_TYPE(startOp) == URX_LB_START); + int32_t dataLoc = URX_VAL(startOp); + int32_t op = URX_BUILD(URX_LBN_END, dataLoc); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + // Determine the min and max bounds for the length of the + // string that the pattern can match. + // An unbounded upper limit is an error. + int32_t patEnd = fRXPat->fCompiledPat->size() - 1; + int32_t minML = minMatchLength(fMatchOpenParen, patEnd); + int32_t maxML = maxMatchLength(fMatchOpenParen, patEnd); + if (maxML == INT32_MAX) { + error(U_REGEX_LOOK_BEHIND_LIMIT); + break; + } + U_ASSERT(minML <= maxML); + + // Insert the min and max match len bounds into the URX_LB_CONT op that + // appears at the top of the look-behind block, at location fMatchOpenParen+1 + fRXPat->fCompiledPat->setElementAt(minML, fMatchOpenParen-3); + fRXPat->fCompiledPat->setElementAt(maxML, fMatchOpenParen-2); + + // Insert the pattern location to continue at after a successful match + // as the last operand of the URX_LBN_CONT + op = URX_BUILD(URX_RELOC_OPRND, fRXPat->fCompiledPat->size()); + fRXPat->fCompiledPat->setElementAt(op, fMatchOpenParen-1); + } + break; + + + + default: + U_ASSERT(FALSE); + } + + // remember the next location in the compiled pattern. + // The compilation of Quantifiers will look at this to see whether its looping + // over a parenthesized block or a single item + fMatchCloseParen = fRXPat->fCompiledPat->size(); +} + + + +//------------------------------------------------------------------------------ +// +// compileSet Compile the pattern operations for a reference to a +// UnicodeSet. +// +//------------------------------------------------------------------------------ +void RegexCompile::compileSet(UnicodeSet *theSet) +{ + if (theSet == NULL) { + return; + } + int32_t setSize = theSet->size(); + UChar32 firstSetChar = theSet->charAt(0); + if (firstSetChar == -1) { + // Sets that contain only strings, but no individual chars, + // will end up here. + error(U_REGEX_SET_CONTAINS_STRING); + setSize = 0; + } + + switch (setSize) { + case 0: + { + // Set of no elements. Always fails to match. + fRXPat->fCompiledPat->addElement(URX_BUILD(URX_BACKTRACK, 0), *fStatus); + delete theSet; + } + break; + + case 1: + { + // The set contains only a single code point. Put it into + // the compiled pattern as a single char operation rather + // than a set, and discard the set itself. + literalChar(firstSetChar); + delete theSet; + } + break; + + default: + { + // The set contains two or more chars. (the normal case) + // Put it into the compiled pattern as a set. + int32_t setNumber = fRXPat->fSets->size(); + fRXPat->fSets->addElement(theSet, *fStatus); + int32_t setOp = URX_BUILD(URX_SETREF, setNumber); + fRXPat->fCompiledPat->addElement(setOp, *fStatus); + } + } +} + + +//------------------------------------------------------------------------------ +// +// compileInterval Generate the code for a {min, max} style interval quantifier. +// Except for the specific opcodes used, the code is the same +// for all three types (greedy, non-greedy, possessive) of +// intervals. The opcodes are supplied as parameters. +// +// The code for interval loops has this form: +// 0 CTR_INIT counter loc (in stack frame) +// 1 5 patt address of CTR_LOOP at bottom of block +// 2 min count +// 3 max count (-1 for unbounded) +// 4 ... block to be iterated over +// 5 CTR_LOOP +// +// In +//------------------------------------------------------------------------------ +void RegexCompile::compileInterval(int32_t InitOp, int32_t LoopOp) +{ + // The CTR_INIT op at the top of the block with the {n,m} quantifier takes + // four slots in the compiled code. Reserve them. + int32_t topOfBlock = blockTopLoc(TRUE); + insertOp(topOfBlock); + insertOp(topOfBlock); + insertOp(topOfBlock); + + // The operands for the CTR_INIT opcode include the index in the matcher data + // of the counter. Allocate it now. + int32_t counterLoc = fRXPat->fFrameSize; + fRXPat->fFrameSize++; + + int32_t op = URX_BUILD(InitOp, counterLoc); + fRXPat->fCompiledPat->setElementAt(op, topOfBlock); + + // The second operand of CTR_INIT is the location following the end of the loop. + // Must put in as a URX_RELOC_OPRND so that the value will be adjusted if the + // compilation of something later on causes the code to grow and the target + // position to move. + int32_t loopEnd = fRXPat->fCompiledPat->size(); + op = URX_BUILD(URX_RELOC_OPRND, loopEnd); + fRXPat->fCompiledPat->setElementAt(op, topOfBlock+1); + + // Followed by the min and max counts. + fRXPat->fCompiledPat->setElementAt(fIntervalLow, topOfBlock+2); + fRXPat->fCompiledPat->setElementAt(fIntervalUpper, topOfBlock+3); + + // Apend the CTR_LOOP op. The operand is the location of the CTR_INIT op. + // Goes at end of the block being looped over, so just append to the code so far. + op = URX_BUILD(LoopOp, topOfBlock); + fRXPat->fCompiledPat->addElement(op, *fStatus); + + if ((fIntervalLow & 0xff000000) != 0 || + fIntervalUpper > 0 && (fIntervalUpper & 0xff000000) != 0) { + error(U_REGEX_NUMBER_TOO_BIG); + } + + if (fIntervalLow > fIntervalUpper && fIntervalUpper != -1) { + error(U_REGEX_MAX_LT_MIN); + } +} + + + +UBool RegexCompile::compileInlineInterval() { + if (fIntervalUpper > 10 || fIntervalUpper < fIntervalLow) { + // Too big to inline. Fail, which will cause looping code to be generated. + // (Upper < Lower picks up unbounded upper and errors, both.) + return FALSE; + } + + int32_t topOfBlock = blockTopLoc(FALSE); + if (fIntervalUpper == 0) { + // Pathological case. Attempt no matches, as if the block doesn't exist. + fRXPat->fCompiledPat->setSize(topOfBlock); + return TRUE; + } + + if (topOfBlock != fRXPat->fCompiledPat->size()-1 && fIntervalUpper != 1) { + // The thing being repeated is not a single op, but some + // more complex block. Do it as a loop, not inlines. + // Note that things "repeated" a max of once are handled as inline, because + // the one copy of the code already generated is just fine. + return FALSE; + } + + // Pick up the opcode that is to be repeated + // + int32_t op = fRXPat->fCompiledPat->elementAti(topOfBlock); + + // Compute the pattern location where the inline sequence + // will end, and set up the state save op that will be needed. + // + int32_t endOfSequenceLoc = fRXPat->fCompiledPat->size()-1 + + fIntervalUpper + (fIntervalUpper-fIntervalLow); + int32_t saveOp = URX_BUILD(URX_STATE_SAVE, endOfSequenceLoc); + if (fIntervalLow == 0) { + insertOp(topOfBlock); + fRXPat->fCompiledPat->setElementAt(saveOp, topOfBlock); + } + + + + // Loop, emitting the op for the thing being repeated each time. + // Loop starts at 1 because one instance of the op already exists in the pattern, + // it was put there when it was originally encountered. + int32_t i; + for (i=1; i<fIntervalUpper; i++ ) { + if (i == fIntervalLow) { + fRXPat->fCompiledPat->addElement(saveOp, *fStatus); + } + if (i > fIntervalLow) { + fRXPat->fCompiledPat->addElement(saveOp, *fStatus); + } + fRXPat->fCompiledPat->addElement(op, *fStatus); + } + return TRUE; +} + + + +//------------------------------------------------------------------------------ +// +// matchStartType Determine how a match can start. +// Used to optimize find() operations. +// +// Operation is very similar to minMatchLength(). Walk the compiled +// pattern, keeping an on-going minimum-match-length. For any +// op where the min match coming in is zero, add that ops possible +// starting matches to the possible starts for the overall pattern. +// +//------------------------------------------------------------------------------ +void RegexCompile::matchStartType() { + if (U_FAILURE(*fStatus)) { + return; + } + + + int32_t loc; // Location in the pattern of the current op being processed. + int32_t op; // The op being processed + int32_t opType; // The opcode type of the op + int32_t currentLen = 0; // Minimum length of a match to this point (loc) in the pattern + int32_t numInitialStrings = 0; // Number of strings encountered that could match at start. + + UBool atStart = TRUE; // True if no part of the pattern yet encountered + // could have advanced the position in a match. + // (Maximum match length so far == 0) + + // forwardedLength is a vector holding minimum-match-length values that + // are propagated forward in the pattern by JMP or STATE_SAVE operations. + // It must be one longer than the pattern being checked because some ops + // will jmp to a end-of-block+1 location from within a block, and we must + // count those when checking the block. + int32_t end = fRXPat->fCompiledPat->size(); + UVector32 forwardedLength(end+1, *fStatus); + forwardedLength.setSize(end+1); + for (loc=3; loc<end; loc++) { + forwardedLength.setElementAt(INT32_MAX, loc); + } + + for (loc = 3; loc<end; loc++) { + op = fRXPat->fCompiledPat->elementAti(loc); + opType = URX_TYPE(op); + + // The loop is advancing linearly through the pattern. + // If the op we are now at was the destination of a branch in the pattern, + // and that path has a shorter minimum length than the current accumulated value, + // replace the current accumulated value. + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + if (forwardedLength.elementAti(loc) < currentLen) { + currentLen = forwardedLength.elementAti(loc); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + + switch (opType) { + // Ops that don't change the total length matched + case URX_RESERVED_OP: + case URX_END: + case URX_STRING_LEN: + case URX_NOP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_Z: + case URX_DOLLAR: + case URX_RELOC_OPRND: + case URX_STO_INP_LOC: + case URX_DOLLAR_M: + case URX_BACKTRACK: + case URX_BACKREF: // BackRef. Must assume that it might be a zero length match + case URX_BACKREF_I: + + case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. + case URX_LD_SP: + break; + + case URX_CARET: + if (atStart) { + fRXPat->fStartType = START_START; + } + break; + + case URX_CARET_M: + if (atStart) { + fRXPat->fStartType = START_LINE; + } + break; + + case URX_ONECHAR: + if (currentLen == 0) { + // This character could appear at the start of a match. + // Add it to the set of possible starting characters. + fRXPat->fInitialChars->add(URX_VAL(op)); + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + + case URX_SETREF: + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + U_ASSERT(sn > 0 && sn < fRXPat->fSets->size()); + const UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(sn); + fRXPat->fInitialChars->addAll(*s); + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + case URX_LOOP_SR_I: + // [Set]*, like a SETREF, above, in what it can match, + // but may not match at all, so currentLen is not incremented. + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + U_ASSERT(sn > 0 && sn < fRXPat->fSets->size()); + const UnicodeSet *s = (UnicodeSet *)fRXPat->fSets->elementAt(sn); + fRXPat->fInitialChars->addAll(*s); + numInitialStrings += 2; + } + atStart = FALSE; + break; + + case URX_LOOP_DOT_I: + if (currentLen == 0) { + // .* at the start of a pattern. + // Any character can begin the match. + fRXPat->fInitialChars->clear(); + fRXPat->fInitialChars->complement(); + numInitialStrings += 2; + } + atStart = FALSE; + break; + + + case URX_STATIC_SETREF: + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + U_ASSERT(sn>0 && sn<URX_LAST_SET); + const UnicodeSet *s = fRXPat->fStaticSets[sn]; + fRXPat->fInitialChars->addAll(*s); + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + + + case URX_STAT_SETREF_N: + if (currentLen == 0) { + int32_t sn = URX_VAL(op); + const UnicodeSet *s = fRXPat->fStaticSets[sn]; + UnicodeSet sc(*s); + sc.complement(); + fRXPat->fInitialChars->addAll(sc); + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + + + case URX_BACKSLASH_D: + // Digit Char + if (currentLen == 0) { + UnicodeSet s; + s.applyIntPropertyValue(UCHAR_GENERAL_CATEGORY_MASK, U_GC_ND_MASK, *fStatus); + if (URX_VAL(op) != 0) { + s.complement(); + } + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + + case URX_ONECHAR_I: + // Case Insensitive Single Character. + if (currentLen == 0) { + UChar32 c = URX_VAL(op); + if (u_hasBinaryProperty(c, UCHAR_CASE_SENSITIVE)) { + // character may have distinct cased forms. Add all of them + // to the set of possible starting match chars. + UnicodeSet s(c, c); + s.closeOver(USET_CASE_INSENSITIVE); + fRXPat->fInitialChars->addAll(s); + } else { + // Char has no case variants. Just add it as-is to the + // set of possible starting chars. + fRXPat->fInitialChars->add(c); + } + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + + case URX_BACKSLASH_X: // Grahpeme Cluster. Minimum is 1, max unbounded. + case URX_DOTANY_ALL: // . matches one or two. + case URX_DOTANY: + case URX_DOTANY_ALL_PL: + case URX_DOTANY_PL: + if (currentLen == 0) { + // These constructs are all bad news when they appear at the start + // of a match. Any character can begin the match. + fRXPat->fInitialChars->clear(); + fRXPat->fInitialChars->complement(); + numInitialStrings += 2; + } + currentLen++; + atStart = FALSE; + break; + + + case URX_JMPX: + loc++; // Except for extra operand on URX_JMPX, same as URX_JMP. + case URX_JMP: + { + int32_t jmpDest = URX_VAL(op); + if (jmpDest < loc) { + // Loop of some kind. Can safely ignore, the worst that will happen + // is that we understate the true minimum length + currentLen = forwardedLength.elementAti(loc+1); + + } else { + // Forward jump. Propagate the current min length to the target loc of the jump. + U_ASSERT(jmpDest <= end+1); + if (forwardedLength.elementAti(jmpDest) > currentLen) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + atStart = FALSE; + break; + + case URX_JMP_SAV: + case URX_JMP_SAV_X: + // Combo of state save to the next loc, + jmp backwards. + // Net effect on min. length computation is nothing. + atStart = FALSE; + break; + + case URX_FAIL: + // Fails are kind of like a branch, except that the min length was + // propagated already, by the state save. + currentLen = forwardedLength.elementAti(loc+1); + atStart = FALSE; + break; + + + case URX_STATE_SAVE: + { + // State Save, for forward jumps, propagate the current minimum. + // of the state save. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + atStart = FALSE; + break; + + + + + case URX_STRING: + { + loc++; + int32_t stringLenOp = fRXPat->fCompiledPat->elementAti(loc); + int32_t stringLen = URX_VAL(stringLenOp); + U_ASSERT(URX_TYPE(stringLenOp) == URX_STRING_LEN); + U_ASSERT(stringLenOp >= 2); + if (currentLen == 0) { + // Add the starting character of this string to the set of possible starting + // characters for this pattern. + int32_t stringStartIdx = URX_VAL(op); + UChar32 c = fRXPat->fLiteralText.char32At(stringStartIdx); + fRXPat->fInitialChars->add(c); + + // Remember this string. After the entire pattern has been checked, + // if nothing else is identified that can start a match, we'll use it. + numInitialStrings++; + fRXPat->fInitialStringIdx = stringStartIdx; + fRXPat->fInitialStringLen = stringLen; + } + + currentLen += stringLen; + atStart = FALSE; + } + break; + + case URX_STRING_I: + { + // Case-insensitive string. Unlike exact-match strings, we won't + // attempt a string search for possible match positions. But we + // do update the set of possible starting characters. + loc++; + int32_t stringLenOp = fRXPat->fCompiledPat->elementAti(loc); + int32_t stringLen = URX_VAL(stringLenOp); + U_ASSERT(URX_TYPE(stringLenOp) == URX_STRING_LEN); + U_ASSERT(stringLenOp >= 2); + if (currentLen == 0) { + // Add the starting character of this string to the set of possible starting + // characters for this pattern. + int32_t stringStartIdx = URX_VAL(op); + UChar32 c = fRXPat->fLiteralText.char32At(stringStartIdx); + UnicodeSet s(c, c); + s.closeOver(USET_CASE_INSENSITIVE); + fRXPat->fInitialChars->addAll(s); + numInitialStrings += 2; // Matching on an initial string not possible. + } + currentLen += stringLen; + atStart = FALSE; + } + break; + + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + { + // Loop Init Ops. These don't change the min length, but they are 4 word ops + // so location must be updated accordingly. + // Loop Init Ops. + // If the min loop count == 0 + // move loc forwards to the end of the loop, skipping over the body. + // If the min count is > 0, + // continue normal processing of the body of the loop. + int32_t loopEndLoc = fRXPat->fCompiledPat->elementAti(loc+1); + loopEndLoc = URX_VAL(loopEndLoc); + int32_t minLoopCount = fRXPat->fCompiledPat->elementAti(loc+2); + if (minLoopCount == 0) { + // Min Loop Count of 0, treat like a forward branch and + // move the current minimum length up to the target + // (end of loop) location. + U_ASSERT(loopEndLoc <= end+1); + if (forwardedLength.elementAti(loopEndLoc) > currentLen) { + forwardedLength.setElementAt(currentLen, loopEndLoc); + } + } + loc+=3; // Skips over operands of CTR_INIT + } + atStart = FALSE; + break; + + + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + // Loop ops. + // The jump is conditional, backwards only. + atStart = FALSE; + break; + + case URX_LOOP_C: + // More loop ops. These state-save to themselves. + // don't change the minimum match + atStart = FALSE; + break; + + + case URX_LA_START: + case URX_LB_START: + { + // Look-around. Scan forward until the matching look-ahead end, + // without processing the look-around block. This is overly pessimistic. + int32_t depth = 0; + for (;;) { + loc++; + op = fRXPat->fCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_LA_START || URX_TYPE(op) == URX_LB_START) { + depth++; + } + if (URX_TYPE(op) == URX_LA_END || URX_TYPE(op)==URX_LBN_END) { + if (depth == 0) { + break; + } + depth--; + } + if (URX_TYPE(op) == URX_STATE_SAVE) { + // Need this because neg lookahead blocks will FAIL to outside + // of the block. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + U_ASSERT(loc <= end); + } + } + break; + + case URX_LA_END: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + U_ASSERT(FALSE); // Shouldn't get here. These ops should be + // consumed by the scan in URX_LA_START and LB_START + + break; + + default: + U_ASSERT(FALSE); + } + + } + + + // We have finished walking through the ops. Check whether some forward jump + // propagated a shorter length to location end+1. + if (forwardedLength.elementAti(end+1) < currentLen) { + currentLen = forwardedLength.elementAti(end+1); + } + + + fRXPat->fInitialChars8->init(fRXPat->fInitialChars); + + + // Sort out what we should check for when looking for candidate match start positions. + // In order of preference, + // 1. Start of input text buffer. + // 2. A literal string. + // 3. Start of line in multi-line mode. + // 4. A single literal character. + // 5. A character from a set of characters. + // + if (fRXPat->fStartType == START_START) { + // Match only at the start of an input text string. + // start type is already set. We're done. + } else if (numInitialStrings == 1 && fRXPat->fMinMatchLen > 0) { + // Match beginning only with a literal string. + UChar32 c = fRXPat->fLiteralText.char32At(fRXPat->fInitialStringIdx); + U_ASSERT(fRXPat->fInitialChars->contains(c)); + fRXPat->fStartType = START_STRING; + fRXPat->fInitialChar = c; + } else if (fRXPat->fStartType == START_LINE) { + // Match at start of line in Multi-Line mode. + // Nothing to do here; everything is already set. + } else if (fRXPat->fMinMatchLen == 0) { + // Zero length match possible. We could start anywhere. + fRXPat->fStartType = START_NO_INFO; + } else if (fRXPat->fInitialChars->size() == 1) { + // All matches begin with the same char. + fRXPat->fStartType = START_CHAR; + fRXPat->fInitialChar = fRXPat->fInitialChars->charAt(0); + U_ASSERT(fRXPat->fInitialChar != (UChar32)-1); + } else if (fRXPat->fInitialChars->contains((UChar32)0, (UChar32)0x10ffff) == FALSE && + fRXPat->fMinMatchLen > 0) { + // Matches start with a set of character smaller than the set of all chars. + fRXPat->fStartType = START_SET; + } else { + // Matches can start with anything + fRXPat->fStartType = START_NO_INFO; + } + + return; +} + + + +//------------------------------------------------------------------------------ +// +// minMatchLength Calculate the length of the shortest string that could +// match the specified pattern. +// Length is in 16 bit code units, not code points. +// +// The calculated length may not be exact. The returned +// value may be shorter than the actual minimum; it must +// never be longer. +// +// start and end are the range of p-code operations to be +// examined. The endpoints are included in the range. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::minMatchLength(int32_t start, int32_t end) { + if (U_FAILURE(*fStatus)) { + return 0; + } + + U_ASSERT(start <= end); + U_ASSERT(end < fRXPat->fCompiledPat->size()); + + + int32_t loc; + int32_t op; + int32_t opType; + int32_t currentLen = 0; + + + // forwardedLength is a vector holding minimum-match-length values that + // are propagated forward in the pattern by JMP or STATE_SAVE operations. + // It must be one longer than the pattern being checked because some ops + // will jmp to a end-of-block+1 location from within a block, and we must + // count those when checking the block. + UVector32 forwardedLength(end+2, *fStatus); + forwardedLength.setSize(end+2); + for (loc=start; loc<=end+1; loc++) { + forwardedLength.setElementAt(INT32_MAX, loc); + } + + for (loc = start; loc<=end; loc++) { + op = fRXPat->fCompiledPat->elementAti(loc); + opType = URX_TYPE(op); + + // The loop is advancing linearly through the pattern. + // If the op we are now at was the destination of a branch in the pattern, + // and that path has a shorter minimum length than the current accumulated value, + // replace the current accumulated value. + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + if (forwardedLength.elementAti(loc) < currentLen) { + currentLen = forwardedLength.elementAti(loc); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + + switch (opType) { + // Ops that don't change the total length matched + case URX_RESERVED_OP: + case URX_END: + case URX_STRING_LEN: + case URX_NOP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_Z: + case URX_CARET: + case URX_DOLLAR: + case URX_RELOC_OPRND: + case URX_STO_INP_LOC: + case URX_DOLLAR_M: + case URX_CARET_M: + case URX_BACKTRACK: + case URX_BACKREF: // BackRef. Must assume that it might be a zero length match + case URX_BACKREF_I: + + case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. + case URX_LD_SP: + + case URX_JMP_SAV: + case URX_JMP_SAV_X: + break; + + + // Ops that match a minimum of one character (one or two 16 bit code units.) + // + case URX_ONECHAR: + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + case URX_SETREF: + case URX_BACKSLASH_D: + case URX_ONECHAR_I: + case URX_BACKSLASH_X: // Grahpeme Cluster. Minimum is 1, max unbounded. + case URX_DOTANY_ALL: // . matches one or two. + case URX_DOTANY: + case URX_DOTANY_PL: + case URX_DOTANY_ALL_PL: + currentLen++; + break; + + + case URX_JMPX: + loc++; // URX_JMPX has an extra operand, ignored here, + // otherwise processed identically to URX_JMP. + case URX_JMP: + { + int32_t jmpDest = URX_VAL(op); + if (jmpDest < loc) { + // Loop of some kind. Can safely ignore, the worst that will happen + // is that we understate the true minimum length + currentLen = forwardedLength.elementAti(loc+1); + } else { + // Forward jump. Propagate the current min length to the target loc of the jump. + U_ASSERT(jmpDest <= end+1); + if (forwardedLength.elementAti(jmpDest) > currentLen) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + break; + + case URX_FAIL: + { + // Fails are kind of like a branch, except that the min length was + // propagated already, by the state save. + currentLen = forwardedLength.elementAti(loc+1); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + break; + + + case URX_STATE_SAVE: + { + // State Save, for forward jumps, propagate the current minimum. + // of the state save. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + break; + + + case URX_STRING: + case URX_STRING_I: + { + loc++; + int32_t stringLenOp = fRXPat->fCompiledPat->elementAti(loc); + currentLen += URX_VAL(stringLenOp); + } + break; + + + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + { + // Loop Init Ops. + // If the min loop count == 0 + // move loc forwards to the end of the loop, skipping over the body. + // If the min count is > 0, + // continue normal processing of the body of the loop. + int32_t loopEndLoc = fRXPat->fCompiledPat->elementAti(loc+1); + loopEndLoc = URX_VAL(loopEndLoc); + int32_t minLoopCount = fRXPat->fCompiledPat->elementAti(loc+2); + if (minLoopCount == 0) { + loc = loopEndLoc; + } else { + loc+=3; // Skips over operands of CTR_INIT + } + } + break; + + + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + // Loop ops. + // The jump is conditional, backwards only. + break; + + case URX_LOOP_SR_I: + case URX_LOOP_DOT_I: + case URX_LOOP_C: + // More loop ops. These state-save to themselves. + // don't change the minimum match - could match nothing at all. + break; + + + case URX_LA_START: + case URX_LB_START: + { + // Look-around. Scan forward until the matching look-ahead end, + // without processing the look-around block. This is overly pessimistic. + // TODO: Positive lookahead could recursively do the block, then continue + // with the longer of the block or the value coming in. + int32_t depth = 0; + for (;;) { + loc++; + op = fRXPat->fCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_LA_START || URX_TYPE(op) == URX_LB_START) { + depth++; + } + if (URX_TYPE(op) == URX_LA_END || URX_TYPE(op)==URX_LBN_END) { + if (depth == 0) { + break; + } + depth--; + } + if (URX_TYPE(op) == URX_STATE_SAVE) { + // Need this because neg lookahead blocks will FAIL to outside + // of the block. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen < forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } + } + + U_ASSERT(loc <= end); + } + } + break; + + case URX_LA_END: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + // Only come here if the matching URX_LA_START or URX_LB_START was not in the + // range being sized, which happens when measuring size of look-behind blocks. + break; + + default: + U_ASSERT(FALSE); + } + + } + + // We have finished walking through the ops. Check whether some forward jump + // propagated a shorter length to location end+1. + if (forwardedLength.elementAti(end+1) < currentLen) { + currentLen = forwardedLength.elementAti(end+1); + U_ASSERT(currentLen>=0 && currentLen < INT32_MAX); + } + + return currentLen; +} + + + +//------------------------------------------------------------------------------ +// +// maxMatchLength Calculate the length of the longest string that could +// match the specified pattern. +// Length is in 16 bit code units, not code points. +// +// The calculated length may not be exact. The returned +// value may be longer than the actual maximum; it must +// never be shorter. +// +//------------------------------------------------------------------------------ +int32_t RegexCompile::maxMatchLength(int32_t start, int32_t end) { + if (U_FAILURE(*fStatus)) { + return 0; + } + U_ASSERT(start <= end); + U_ASSERT(end < fRXPat->fCompiledPat->size()); + + + int32_t loc; + int32_t op; + int32_t opType; + int32_t currentLen = 0; + UVector32 forwardedLength(end+1, *fStatus); + forwardedLength.setSize(end+1); + + for (loc=start; loc<=end; loc++) { + forwardedLength.setElementAt(0, loc); + } + + for (loc = start; loc<=end; loc++) { + op = fRXPat->fCompiledPat->elementAti(loc); + opType = URX_TYPE(op); + + // The loop is advancing linearly through the pattern. + // If the op we are now at was the destination of a branch in the pattern, + // and that path has a longer maximum length than the current accumulated value, + // replace the current accumulated value. + if (forwardedLength.elementAti(loc) > currentLen) { + currentLen = forwardedLength.elementAti(loc); + } + + switch (opType) { + // Ops that don't change the total length matched + case URX_RESERVED_OP: + case URX_END: + case URX_STRING_LEN: + case URX_NOP: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_Z: + case URX_CARET: + case URX_DOLLAR: + case URX_RELOC_OPRND: + case URX_STO_INP_LOC: + case URX_DOLLAR_M: + case URX_CARET_M: + case URX_BACKTRACK: + + case URX_STO_SP: // Setup for atomic or possessive blocks. Doesn't change what can match. + case URX_LD_SP: + + case URX_LB_END: + case URX_LB_CONT: + case URX_LBN_CONT: + case URX_LBN_END: + break; + + + // Ops that increase that cause an unbounded increase in the length + // of a matched string, or that increase it a hard to characterize way. + // Call the max length unbounded, and stop further checking. + case URX_BACKREF: // BackRef. Must assume that it might be a zero length match + case URX_BACKREF_I: + case URX_BACKSLASH_X: // Grahpeme Cluster. Minimum is 1, max unbounded. + case URX_DOTANY_PL: + case URX_DOTANY_ALL_PL: + currentLen = INT32_MAX; + break; + + + // Ops that match a max of one character (possibly two 16 bit code units.) + // + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + case URX_SETREF: + case URX_BACKSLASH_D: + case URX_ONECHAR_I: + case URX_DOTANY_ALL: + case URX_DOTANY: + currentLen+=2; + break; + + // Single literal character. Increase current max length by one or two, + // depending on whether the char is in the supplementary range. + case URX_ONECHAR: + currentLen++; + if (URX_VAL(op) > 0x10000) { + currentLen++; + } + break; + + // Jumps. + // + case URX_JMP: + case URX_JMPX: + case URX_JMP_SAV: + case URX_JMP_SAV_X: + { + int32_t jmpDest = URX_VAL(op); + if (jmpDest < loc) { + // Loop of some kind. Max match length is unbounded. + currentLen = INT32_MAX; + } else { + // Forward jump. Propagate the current min length to the target loc of the jump. + if (forwardedLength.elementAti(jmpDest) < currentLen) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + currentLen = 0; + } + } + break; + + case URX_FAIL: + // Fails are kind of like a branch, except that the max length was + // propagated already, by the state save. + currentLen = forwardedLength.elementAti(loc+1); + break; + + + case URX_STATE_SAVE: + { + // State Save, for forward jumps, propagate the current minimum. + // of the state save. + // For backwards jumps, they create a loop, maximum + // match length is unbounded. + int32_t jmpDest = URX_VAL(op); + if (jmpDest > loc) { + if (currentLen > forwardedLength.elementAti(jmpDest)) { + forwardedLength.setElementAt(currentLen, jmpDest); + } + } else { + currentLen = INT32_MAX; + } + } + break; + + + + + case URX_STRING: + case URX_STRING_I: + { + loc++; + int32_t stringLenOp = fRXPat->fCompiledPat->elementAti(loc); + currentLen += URX_VAL(stringLenOp); + } + break; + + + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + case URX_LOOP_SR_I: + case URX_LOOP_DOT_I: + case URX_LOOP_C: + // For anything to do with loops, make the match length unbounded. + // Note: INIT instructions are multi-word. Can ignore because + // INT32_MAX length will stop the per-instruction loop. + currentLen = INT32_MAX; + break; + + + + case URX_LA_START: + case URX_LA_END: + // Look-ahead. Just ignore, treat the look-ahead block as if + // it were normal pattern. Gives a too-long match length, + // but good enough for now. + break; + + // End of look-ahead ops should always be consumed by the processing at + // the URX_LA_START op. + // U_ASSERT(FALSE); + // break; + + case URX_LB_START: + { + // Look-behind. Scan forward until the matching look-around end, + // without processing the look-behind block. + int32_t depth = 0; + for (;;) { + loc++; + op = fRXPat->fCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_LA_START || URX_TYPE(op) == URX_LB_START) { + depth++; + } + if (URX_TYPE(op) == URX_LA_END || URX_TYPE(op)==URX_LBN_END) { + if (depth == 0) { + break; + } + depth--; + } + U_ASSERT(loc < end); + } + } + break; + + default: + U_ASSERT(FALSE); + } + + + if (currentLen == INT32_MAX) { + // The maximum length is unbounded. + // Stop further processing of the pattern. + break; + } + + } + return currentLen; + +} + + +//------------------------------------------------------------------------------ +// +// stripNOPs Remove any NOP operations from the compiled pattern code. +// Extra NOPs are inserted for some constructs during the initial +// code generation to provide locations that may be patched later. +// Many end up unneeded, and are removed by this function. +// +//------------------------------------------------------------------------------ +void RegexCompile::stripNOPs() { + + if (U_FAILURE(*fStatus)) { + return; + } + + int32_t end = fRXPat->fCompiledPat->size(); + UVector32 deltas(end, *fStatus); + + // Make a first pass over the code, computing the amount that things + // will be offset at each location in the original code. + int32_t loc; + int32_t d = 0; + for (loc=0; loc<end; loc++) { + deltas.addElement(d, *fStatus); + int32_t op = fRXPat->fCompiledPat->elementAti(loc); + if (URX_TYPE(op) == URX_NOP) { + d++; + } + } + + // Make a second pass over the code, removing the NOPs by moving following + // code up, and patching operands that refer to code locations that + // are being moved. The array of offsets from the first step is used + // to compute the new operand values. + int32_t src; + int32_t dst = 0; + for (src=0; src<end; src++) { + int32_t op = fRXPat->fCompiledPat->elementAti(src); + int32_t opType = URX_TYPE(op); + switch (opType) { + case URX_NOP: + break; + + case URX_STATE_SAVE: + case URX_JMP: + case URX_CTR_LOOP: + case URX_CTR_LOOP_NG: + case URX_RELOC_OPRND: + case URX_JMPX: + case URX_JMP_SAV: + case URX_JMP_SAV_X: + // These are instructions with operands that refer to code locations. + { + int32_t operandAddress = URX_VAL(op); + U_ASSERT(operandAddress>=0 && operandAddress<deltas.size()); + int32_t fixedOperandAddress = operandAddress - deltas.elementAti(operandAddress); + op = URX_BUILD(opType, fixedOperandAddress); + fRXPat->fCompiledPat->setElementAt(op, dst); + dst++; + break; + } + + case URX_RESERVED_OP: + case URX_RESERVED_OP_N: + case URX_BACKTRACK: + case URX_END: + case URX_ONECHAR: + case URX_STRING: + case URX_STRING_LEN: + case URX_START_CAPTURE: + case URX_END_CAPTURE: + case URX_STATIC_SETREF: + case URX_STAT_SETREF_N: + case URX_SETREF: + case URX_DOTANY: + case URX_FAIL: + case URX_BACKSLASH_B: + case URX_BACKSLASH_BU: + case URX_BACKSLASH_G: + case URX_BACKSLASH_X: + case URX_BACKSLASH_Z: + case URX_DOTANY_ALL: + case URX_DOTANY_ALL_PL: + case URX_DOTANY_PL: + case URX_BACKSLASH_D: + case URX_CARET: + case URX_DOLLAR: + case URX_CTR_INIT: + case URX_CTR_INIT_NG: + case URX_STO_SP: + case URX_LD_SP: + case URX_BACKREF: + case URX_STO_INP_LOC: + case URX_LA_START: + case URX_LA_END: + case URX_ONECHAR_I: + case URX_STRING_I: + case URX_BACKREF_I: + case URX_DOLLAR_M: + case URX_CARET_M: + case URX_LB_START: + case URX_LB_CONT: + case URX_LB_END: + case URX_LBN_CONT: + case URX_LBN_END: + case URX_LOOP_SR_I: + case URX_LOOP_DOT_I: + case URX_LOOP_C: + // These instructions are unaltered by the relocation. + fRXPat->fCompiledPat->setElementAt(op, dst); + dst++; + break; + + default: + // Some op is unaccounted for. + U_ASSERT(FALSE); + error(U_REGEX_INTERNAL_ERROR); + } + } + + fRXPat->fCompiledPat->setSize(dst); +} + + + + +//------------------------------------------------------------------------------ +// +// OptDotStar Optimize patterns that end with a '.*' or '.+' to +// just advance the input to the end. +// +// Transform this compiled sequence +// [DOT_ANY | DOT_ANY_ALL] +// JMP_SAV to previous instruction +// [NOP | END_CAPTURE | DOLLAR | BACKSLASH_Z]* +// END +// +// To +// NOP +// [DOT_ANY_PL | DOT_ANY_ALL_PL] +// [NOP | END_CAPTURE | DOLLAR | BACKSLASH_Z]* +// END +// +//------------------------------------------------------------------------------ +void RegexCompile::OptDotStar() { + // Scan backwards in the pattern, looking for a JMP_SAV near the end. + int32_t jmpLoc; + int32_t op = 0; + int32_t opType; + for (jmpLoc=fRXPat->fCompiledPat->size(); jmpLoc--;) { + U_ASSERT(jmpLoc>0); + op = fRXPat->fCompiledPat->elementAti(jmpLoc); + opType = URX_TYPE(op); + switch(opType) { + + + case URX_END: + case URX_NOP: + case URX_END_CAPTURE: + case URX_DOLLAR_M: + case URX_DOLLAR: + case URX_BACKSLASH_Z: + // These ops may follow the JMP_SAV without preventing us from + // doing this optimization. + continue; + + case URX_JMP_SAV: + // Got a trailing JMP_SAV that's a candidate for optimization. + break; + + default: + // This optimization not possible. + return; + } + break; // from the for loop. + } + + // We found in URX_JMP_SAV near the end that is a candidate for optimizing. + // Is the target address the previous instruction? + // Is the previous instruction a flavor of URX_DOTANY + int32_t loopTopLoc = URX_VAL(op); + if (loopTopLoc != jmpLoc-1) { + return; + } + int32_t newOp; + int32_t oldOp = fRXPat->fCompiledPat->elementAti(loopTopLoc); + int32_t oldOpType = opType = URX_TYPE(oldOp); + if (oldOpType == URX_DOTANY) { + newOp = URX_BUILD(URX_DOTANY_PL, 0); + } + else if (oldOpType == URX_DOTANY_ALL) { + newOp = URX_BUILD(URX_DOTANY_ALL_PL, 0); + } else { + return; // Sequence we were looking for isn't there. + } + + // Substitute the new instructions into the pattern. + // The NOP will be removed in a later optimization step. + fRXPat->fCompiledPat->setElementAt(URX_BUILD(URX_NOP, 0), loopTopLoc); + fRXPat->fCompiledPat->setElementAt(newOp, jmpLoc); +} + + +//------------------------------------------------------------------------------ +// +// Error Report a rule parse error. +// Only report it if no previous error has been recorded. +// +//------------------------------------------------------------------------------ +void RegexCompile::error(UErrorCode e) { + if (U_SUCCESS(*fStatus)) { + *fStatus = e; + fParseErr->line = fLineNum; + fParseErr->offset = fCharNum; + + // Fill in the context. + // Note: extractBetween() pins supplied indicies to the string bounds. + uprv_memset(fParseErr->preContext, 0, sizeof(fParseErr->preContext)); + uprv_memset(fParseErr->postContext, 0, sizeof(fParseErr->postContext)); + fRXPat->fPattern.extractBetween(fScanIndex-U_PARSE_CONTEXT_LEN+1, fScanIndex, + fParseErr->preContext, 0); + fRXPat->fPattern.extractBetween(fScanIndex, fScanIndex+U_PARSE_CONTEXT_LEN-1, + fParseErr->postContext, 0); + } +} + + +// +// Assorted Unicode character constants. +// Numeric because there is no portable way to enter them as literals. +// (Think EBCDIC). +// +static const UChar chCR = 0x0d; // New lines, for terminating comments. +static const UChar chLF = 0x0a; +static const UChar chNEL = 0x85; // NEL newline variant +static const UChar chLS = 0x2028; // Unicode Line Separator +static const UChar chPound = 0x23; // '#', introduces a comment. +static const UChar chE = 0x45; // 'E' +static const UChar chUpperN = 0x4E; +static const UChar chLowerP = 0x70; +static const UChar chUpperP = 0x50; +static const UChar chBackSlash = 0x5c; // '\' introduces a char escape +static const UChar chLBracket = 0x5b; +static const UChar chRBracket = 0x5d; +static const UChar chRBrace = 0x7d; + + +//------------------------------------------------------------------------------ +// +// nextCharLL Low Level Next Char from the regex pattern. +// Get a char from the string, keep track of input position +// for error reporting. +// +//------------------------------------------------------------------------------ +UChar32 RegexCompile::nextCharLL() { + UChar32 ch; + UnicodeString &pattern = fRXPat->fPattern; + + if (fPeekChar != -1) { + ch = fPeekChar; + fPeekChar = -1; + return ch; + } + if (fPatternLength==0 || fNextIndex >= fPatternLength) { + return (UChar32)-1; + } + ch = pattern.char32At(fNextIndex); + fNextIndex = pattern.moveIndex32(fNextIndex, 1); + + if (ch == chCR || + ch == chNEL || + ch == chLS || + ch == chLF && fLastChar != chCR) { + // Character is starting a new line. Bump up the line number, and + // reset the column to 0. + fLineNum++; + fCharNum=0; + if (fQuoteMode) { + error(U_REGEX_RULE_SYNTAX); + fQuoteMode = FALSE; + } + } + else { + // Character is not starting a new line. Except in the case of a + // LF following a CR, increment the column position. + if (ch != chLF) { + fCharNum++; + } + } + fLastChar = ch; + return ch; +} + +//------------------------------------------------------------------------------ +// +// peekCharLL Low Level Character Scanning, sneak a peek at the next +// character without actually getting it. +// +//------------------------------------------------------------------------------ +UChar32 RegexCompile::peekCharLL() { + if (fPeekChar == -1) { + fPeekChar = nextCharLL(); + } + return fPeekChar; +} + + +//------------------------------------------------------------------------------ +// +// nextChar for pattern scanning. At this level, we handle stripping +// out comments and processing some backslash character escapes. +// The rest of the pattern grammar is handled at the next level up. +// +//------------------------------------------------------------------------------ +void RegexCompile::nextChar(RegexPatternChar &c) { + + fScanIndex = fNextIndex; + c.fChar = nextCharLL(); + c.fQuoted = FALSE; + + if (fQuoteMode) { + c.fQuoted = TRUE; + if ((c.fChar==chBackSlash && peekCharLL()==chE) || c.fChar == (UChar32)-1) { + fQuoteMode = FALSE; // Exit quote mode, + nextCharLL(); // discard the E + nextChar(c); // recurse to get the real next char + } + } + else if (fInBackslashQuote) { + // The current character immediately follows a '\' + // Don't check for any further escapes, just return it as-is. + // Don't set c.fQuoted, because that would prevent the state machine from + // dispatching on the character. + fInBackslashQuote = FALSE; + } + else + { + // We are not in a \Q quoted region \E of the source. + // + if (fModeFlags & UREGEX_COMMENTS) { + // + // We are in free-spacing and comments mode. + // Scan through any white space and comments, until we + // reach a significant character or the end of inut. + for (;;) { + if (c.fChar == (UChar32)-1) { + break; // End of Input + } + if (c.fChar == chPound && fEOLComments == TRUE) { + // Start of a comment. Consume the rest of it, until EOF or a new line + for (;;) { + c.fChar = nextCharLL(); + if (c.fChar == (UChar32)-1 || // EOF + c.fChar == chCR || + c.fChar == chLF || + c.fChar == chNEL || + c.fChar == chLS) { + break; + } + } + } + if (uprv_isRuleWhiteSpace(c.fChar) == FALSE) { + break; + } + c.fChar = nextCharLL(); + } + } + + // + // check for backslash escaped characters. + // + int32_t startX = fNextIndex; // start and end positions of the + int32_t endX = fNextIndex; // sequence following the '\' + if (c.fChar == chBackSlash) { + if (RegexStaticSets::gStaticSets->fUnescapeCharSet.contains(peekCharLL())) { + // + // A '\' sequence that is handled by ICU's standard unescapeAt function. + // Includes \uxxxx, \n, \r, many others. + // Return the single equivalent character. + // + nextCharLL(); // get & discard the peeked char. + c.fQuoted = TRUE; + c.fChar = fRXPat->fPattern.unescapeAt(endX); + if (startX == endX) { + error(U_REGEX_BAD_ESCAPE_SEQUENCE); + } + fCharNum += endX - startX; + fNextIndex = endX; + } + else + { + // We are in a '\' escape that will be handled by the state table scanner. + // Just return the backslash, but remember that the following char is to + // be taken literally. TODO: this is awkward, think about alternatives. + fInBackslashQuote = TRUE; + } + } + } + + // re-enable # to end-of-line comments, in case they were disabled. + // They are disabled by the parser upon seeing '(?', but this lasts for + // the fetching of the next character only. + fEOLComments = TRUE; + + // putc(c.fChar, stdout); +} + + + +//------------------------------------------------------------------------------ +// +// scanSet Construct a UnicodeSet from the text at the current scan +// position. Advance the scan position to the first character +// after the set. +// +// The scan position is normally under the control of the state machine +// that controls pattern parsing. UnicodeSets, however, are parsed by +// the UnicodeSet constructor, not by the Regex pattern parser. +// +//------------------------------------------------------------------------------ +UnicodeSet *RegexCompile::scanSet() { + UnicodeSet *uset = NULL; + ParsePosition pos; + int i; + + if (U_FAILURE(*fStatus)) { + return NULL; + } + + pos.setIndex(fScanIndex); + UErrorCode localStatus = U_ZERO_ERROR; + uint32_t usetFlags = 0; + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + usetFlags |= USET_CASE_INSENSITIVE; + } + if (fModeFlags & UREGEX_COMMENTS) { + usetFlags |= USET_IGNORE_SPACE; + } + + uset = new UnicodeSet(fRXPat->fPattern, pos, + usetFlags, NULL, localStatus); + if (U_FAILURE(localStatus)) { + // TODO: Get more accurate position of the error from UnicodeSet's return info. + // UnicodeSet appears to not be reporting correctly at this time. + REGEX_SCAN_DEBUG_PRINTF(("UnicodeSet parse postion.ErrorIndex = %d\n", pos.getIndex())); + error(localStatus); + delete uset; + return NULL; + } + + // Advance the current scan postion over the UnicodeSet. + // Don't just set fScanIndex because the line/char positions maintained + // for error reporting would be thrown off. + i = pos.getIndex(); + for (;;) { + if (fNextIndex >= i) { + break; + } + nextCharLL(); + } + + return uset; +} + + +//------------------------------------------------------------------------------ +// +// scanProp Construct a UnicodeSet from the text at the current scan +// position, which will be of the form \p{whaterver} +// +// The scan position will be at the 'p' or 'P'. On return +// the scan position should be just after the '}' +// +// Return a UnicodeSet, constructed from the \P pattern, +// or NULL if the pattern is invalid. +// +//------------------------------------------------------------------------------ +UnicodeSet *RegexCompile::scanProp() { + UnicodeSet *uset = NULL; + + if (U_FAILURE(*fStatus)) { + return NULL; + } + + U_ASSERT(fC.fChar == chLowerP || fC.fChar == chUpperP || fC.fChar == chUpperN); + + // enclose the \p{property} from the regex pattern source in [brackets] + UnicodeString setPattern; + setPattern.append(chLBracket); + setPattern.append(chBackSlash); + for (;;) { + setPattern.append(fC.fChar); + if (fC.fChar == chRBrace) { + break; + } + nextChar(fC); + if (fC.fChar == -1) { + // Hit the end of the input string without finding the closing '}' + error(U_REGEX_PROPERTY_SYNTAX); + return NULL; + } + } + setPattern.append(chRBracket); + + uint32_t usetFlags = 0; + if (fModeFlags & UREGEX_CASE_INSENSITIVE) { + usetFlags |= USET_CASE_INSENSITIVE; + } + if (fModeFlags & UREGEX_COMMENTS) { + usetFlags |= USET_IGNORE_SPACE; + } + + // Build the UnicodeSet from the set pattern we just built up in a string. + uset = new UnicodeSet(setPattern, usetFlags, NULL, *fStatus); + if (U_FAILURE(*fStatus)) { + delete uset; + uset = NULL; + } + + nextChar(fC); // Continue overall regex pattern processing with char after the '}' + return uset; +} + +U_NAMESPACE_END +#endif // !UCONFIG_NO_REGULAR_EXPRESSIONS + |