From 1d526576c9cc2beca0ebf567ed381cca0d45fce2 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Sun, 27 Nov 2016 13:25:39 +0100 Subject: [PATCH 1/2] + Integration of DeelX RegExpr engine ( see http://www.regexlab.com/en/deelx/ ) Notepad2 and also the maintaining fork notepad2-mod are using Scintilla's internal regexpr engine, which has its limitations ( see XhmikosR/notepad2-mod#148 ). In wise forsight, the developer of Scintilla creates an interface (activated by preprocessor define SCI_OWNREGEX), to embed your own RegExpr search (and replace) engine. --- scintilla/Scintilla.vcxproj | 13 +- scintilla/Scintilla.vcxproj.filters | 30 +- scintilla/deelx/DeelxRegexSearch.cxx | 330 ++ scintilla/deelx/deelx64.h | 4830 +++++++++++++++++ ...- Regular Expression Engine - Features.url | 2 + ...lx Regular Expression Syntax Reference.txt | 426 ++ ...lx Regular Expression Syntax Reference.url | 2 + scintilla/deelx/doc/deelx_en.chm | Bin 0 -> 76604 bytes scintilla/deelx/doc/orig_src/deelx12.h | 4665 ++++++++++++++++ scintilla/deelx/doc/orig_src/deelx13.h | 4804 ++++++++++++++++ src/Notepad3.rc | 2 +- 11 files changed, 15098 insertions(+), 6 deletions(-) create mode 100644 scintilla/deelx/DeelxRegexSearch.cxx create mode 100644 scintilla/deelx/deelx64.h create mode 100644 scintilla/deelx/doc/Deelx - Regular Expression Engine - Features.url create mode 100644 scintilla/deelx/doc/Deelx Regular Expression Syntax Reference.txt create mode 100644 scintilla/deelx/doc/Deelx Regular Expression Syntax Reference.url create mode 100644 scintilla/deelx/doc/deelx_en.chm create mode 100644 scintilla/deelx/doc/orig_src/deelx12.h create mode 100644 scintilla/deelx/doc/orig_src/deelx13.h diff --git a/scintilla/Scintilla.vcxproj b/scintilla/Scintilla.vcxproj index 4e7822ee6..cd26e7b95 100644 --- a/scintilla/Scintilla.vcxproj +++ b/scintilla/Scintilla.vcxproj @@ -121,7 +121,7 @@ true Disabled NotUsing - _SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) + _SCL_SECURE_NO_WARNINGS;WIN32;SCI_OWNREGEX;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) MultiThreadedDebug Level3 @@ -134,7 +134,7 @@ true Disabled NotUsing - _SCL_SECURE_NO_WARNINGS;_WIN64;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) + _SCL_SECURE_NO_WARNINGS;_WIN64;SCI_OWNREGEX;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) MultiThreadedDebug Level3 @@ -150,7 +150,7 @@ true MaxSpeed NotUsing - _SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) + _SCL_SECURE_NO_WARNINGS;WIN32;SCI_OWNREGEX;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) MultiThreaded Level3 @@ -162,7 +162,7 @@ true MaxSpeed NotUsing - _SCL_SECURE_NO_WARNINGS;_WIN64;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) + _SCL_SECURE_NO_WARNINGS;_WIN64;SCI_OWNREGEX;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES;STATIC_BUILD;SCI_LEXER;USE_D2D;%(PreprocessorDefinitions) MultiThreaded Level3 @@ -171,6 +171,7 @@ + @@ -249,6 +250,7 @@ + @@ -304,6 +306,9 @@ + + + diff --git a/scintilla/Scintilla.vcxproj.filters b/scintilla/Scintilla.vcxproj.filters index e49186e26..a944b9d5d 100644 --- a/scintilla/Scintilla.vcxproj.filters +++ b/scintilla/Scintilla.vcxproj.filters @@ -16,6 +16,12 @@ {afe7e35e-cd81-406c-a770-df29d2b3fc95} + + {67242aad-9133-44e7-9774-c36f5a9194bc} + + + {4e167b73-0447-4a31-a66b-64c2d684516d} + @@ -237,6 +243,18 @@ win32 + + deelx + + + lexers + + + lexers + + + lexers + @@ -398,6 +416,16 @@ win32 - + + deelx + + + include + + + + + deelx\doc + \ No newline at end of file diff --git a/scintilla/deelx/DeelxRegexSearch.cxx b/scintilla/deelx/DeelxRegexSearch.cxx new file mode 100644 index 000000000..d7a3875fb --- /dev/null +++ b/scintilla/deelx/DeelxRegexSearch.cxx @@ -0,0 +1,330 @@ +/** + * @file DeelxRegexSearch.cxx + * @brief integrate DeelX regex searching for Scintilla library + * (Scintilla Lib is copyright 1998-2016 by Neil Hodgson ) + * + * uses DEELX - Regular Expression Engine (v1.3) (deelx.h) - http://www.regexlab.com/deelx/ + * download: http://www.regexlab.com/download/deelx/deelx.zip (v1.2) + * or : https://github.com/AndreasMartin72/mksqlite/blob/master/deelx/deelx.h (v1.3) + * (Copyright Announcement: Free to use/redistribute. Provenance must be declared when redistributed) + * API documentation see accompanying "deelx_en.chm" HTML Help. + * + * @autor Rainer Kottenhoff (RaPeHoff) + * + * Install: + * - place files (deelx64.h, DeelxRegexSearch.cxx, deelx_en.chm) + * in a directory (deelx) within the scintilla project (.../scintilla/deelx/) + * - add source files to scintilla project (Scintilla.vcxproj in VS) + * - define compiler (preprocessor) macro for scintilla project named "SCI_OWNREGEX" + * -> this will switch from scintilla's buildin regex engine to deelx's regex engine + * - recompile and link scintilla library + * - build application + */ + +#ifdef SCI_OWNREGEX + +#include +#include +#include + +#pragma warning( push ) +#pragma warning( disable : 4996 ) // Scintilla's "unsafe" use of std::copy() (SplitVector.h) +// // or use -D_SCL_SECURE_NO_WARNINGS preprocessor define + +#include "Platform.h" +#include "Scintilla.h" +#include "ILexer.h" +#include "SplitVector.h" +#include "Partitioning.h" +#include "CellBuffer.h" +#include "CaseFolder.h" +#include "RunStyles.h" +#include "Decoration.h" +#include "CharClassify.h" +#include "Document.h" +// --------------------------------------------------------------- +#include "deelx64.h" // DEELX - Regular Expression Engine (v1.3) +// --------------------------------------------------------------- + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +class DeelxRegexSearch : public RegexSearchBase +{ +public: + + explicit DeelxRegexSearch(CharClassify* charClassTable) + : m_RegExpr() + , m_Match() + , m_MatchPos(-1) + , m_MatchLength(0) + , m_pContext(nullptr) + , m_SubstitutionBuffer(nullptr) + {} + + virtual ~DeelxRegexSearch() + { + ReleaseSubstitutionBuffer(); + ReleaseContext(); + } + + virtual long FindText(Document* doc, int minPos, int maxPos, const char* pattern, + bool caseSensitive, bool word, bool wordStart, int flags, int* length) override; + + virtual const char* SubstituteByPosition(Document* doc, const char* text, int* length) override; + + +private: + + inline void ReleaseContext() + { + if (m_pContext != nullptr) { + m_RegExpr.ReleaseContext(m_pContext); + m_pContext = nullptr; + } + } + + inline void ReleaseSubstitutionBuffer() + { + if (m_SubstitutionBuffer) { + m_RegExpr.ReleaseString(m_SubstitutionBuffer); + m_SubstitutionBuffer = nullptr; + } + } + +private: + deelx::CRegexpT m_RegExpr; + deelx::MatchResult m_Match; + deelx::index_t m_MatchPos; + deelx::index_t m_MatchLength; + deelx::CContext* m_pContext; + char* m_SubstitutionBuffer; +}; +// ============================================================================ + + +#ifdef SCI_NAMESPACE +RegexSearchBase *Scintilla::CreateRegexSearch(CharClassify *charClassTable) +{ + return new DeelxRegexSearch(charClassTable); +} +#else +RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable) +{ + return new DeelxRegexSearch(charClassTable); +} +#endif + +// ============================================================================ + +/** + * forward declaration of utility functions + */ +std::string& translateRegExpr(std::string& regExprStr, bool wholeWord, bool wordStart); +std::string& convertReplExpr(std::string& replStr); + + +// ============================================================================ + + +/** + * Find text in document, supporting both forward and backward + * searches (just pass minPos > maxPos to do a backward search) + * Has not been tested with backwards DBCS searches yet. + */ +long DeelxRegexSearch::FindText(Document* doc, int minPos, int maxPos, const char *pattern, + bool caseSensitive, bool word, bool wordStart, int searchFlags, int *length) +{ + int startPos, endPos; + bool left2right; + + if (minPos <= maxPos) { + left2right = true; + startPos = minPos; + endPos = maxPos; + } + else { // backward search + left2right = false; + startPos = maxPos; + endPos = minPos; + } + + // Range endpoints should not be inside DBCS characters, but just in case, move them. + startPos = doc->MovePositionOutsideChar(startPos, 1, false); + endPos = doc->MovePositionOutsideChar(endPos, 1, false); + + int compileFlags(deelx::MULTILINE | deelx::GLOBAL | deelx::EXTENDED); // the .(dot) does not match line-breaks + //int compileFlags(deelx::SINGLELINE | deelx::MULTILINE | deelx::GLOBAL | deelx::EXTENDED); // the .(dot) also matches line-breaks + compileFlags |= (caseSensitive) ? deelx::NO_FLAG : deelx::IGNORECASE; + compileFlags |= (left2right) ? deelx::NO_FLAG : deelx::RIGHTTOLEFT; + + std::string sRegExprStrg = translateRegExpr(std::string(pattern, *length), word, wordStart); + + try { + m_RegExpr.Compile(sRegExprStrg.c_str(), compileFlags); + } + catch (...) { + return -2; // -1 is normally used for not found, -2 is used here for invalid regex + } + + int rangeLen = endPos - startPos; + int searchStartPos = left2right ? 0 : rangeLen; + ReleaseContext(); + m_pContext = m_RegExpr.PrepareMatch(doc->RangePointer(startPos, rangeLen), searchStartPos); + + m_Match = m_RegExpr.Match(m_pContext); + + m_MatchPos = -1; // not found + m_MatchLength = 0; + if (m_Match.IsMatched()) { + m_MatchPos = startPos + m_Match.GetStart(); + m_MatchLength = (m_Match.GetEnd() - m_Match.GetStart()); + } + + //NOTE: potential 64-bit-size issue at interface here: + *length = static_cast(m_MatchLength); + return static_cast(m_MatchPos); +} +// ============================================================================ + + +const char* DeelxRegexSearch::SubstituteByPosition(Document* doc, const char* text, int* length) +{ + if (!m_Match.IsMatched() || (m_MatchPos < 0)) { + *length = 0; + return nullptr; + } + std::string sReplStrg = convertReplExpr(std::string(text, *length)); + + //NOTE: potential 64-bit-size issue at interface here: + const char* pString = doc->RangePointer(static_cast(m_MatchPos), static_cast(m_MatchLength)); + + deelx::index_t resLength; + ReleaseSubstitutionBuffer(); + m_SubstitutionBuffer = m_RegExpr.Replace(pString, m_MatchLength, sReplStrg.c_str(), + static_cast(sReplStrg.length()), resLength); + + //NOTE: potential 64-bit-size issue at interface here: + *length = static_cast(resLength); + + return m_SubstitutionBuffer; +} +// ============================================================================ + + + + +// ============================================================================ +// Some Helpers +// ============================================================================ + + +void replaceAll(std::string& source, const std::string& from, const std::string& to) +{ + std::string newString; + newString.reserve(source.length() * 2); // avoids a few memory allocations + + std::string::size_type lastPos = 0; + std::string::size_type findPos; + + while (std::string::npos != (findPos = source.find(from, lastPos))) { + newString.append(source, lastPos, findPos - lastPos); + newString += to; + lastPos = findPos + from.length(); + } + // Care for the rest after last occurrence + newString += source.substr(lastPos); + + source.swap(newString); +} +// ---------------------------------------------------------------------------- + + + +std::string& translateRegExpr(std::string& regExprStr, bool wholeWord, bool wordStart) +{ + std::string tmpStr; + + if (wholeWord || wordStart) { // push '\b' at the begin of regexpr + tmpStr.push_back('\\'); + tmpStr.push_back('b'); + tmpStr.append(regExprStr); + if (wholeWord) { // push '\b' at the end of regexpr + tmpStr.push_back('\\'); + tmpStr.push_back('b'); + } + replaceAll(tmpStr, ".", "\\w"); + } + else { + tmpStr.append(regExprStr); + } + std::swap(regExprStr, tmpStr); + return regExprStr; +} +// ---------------------------------------------------------------------------- + + + +std::string& convertReplExpr(std::string& replStr) +{ + std::string tmpStr; + for (size_t i = 0; i < replStr.length(); ++i) { + char ch = replStr[i]; + if (ch == '\\') { + ch = replStr[++i]; // next char + if (ch == '\\') { + // skip 2nd backslash ("\\") + if (i < replStr.length()) { ch = replStr[++i]; } + else { break; } + } + if (ch >= '1' && ch <= '9') { + // former behavior convenience: + // change "\\" to deelx's group reference ($) + tmpStr.push_back('$'); + } + switch (ch) { + // check for escape seq: + case 'a': + tmpStr.push_back('\a'); + break; + case 'b': + tmpStr.push_back('\b'); + break; + case 'f': + tmpStr.push_back('\f'); + break; + case 'n': + tmpStr.push_back('\n'); + break; + case 'r': + tmpStr.push_back('\r'); + break; + case 't': + tmpStr.push_back('\t'); + break; + case 'v': + tmpStr.push_back('\v'); + break; + case '\\': + tmpStr.push_back('\\'); + break; + default: + // unknown ctrl seq + tmpStr.push_back(ch); + break; + } + } + else { + tmpStr.push_back(ch); + } + } //for + + std::swap(replStr, tmpStr); + return replStr; +} +// ============================================================================ + +#pragma warning( pop ) + +#endif //SCI_OWNREGEX diff --git a/scintilla/deelx/deelx64.h b/scintilla/deelx/deelx64.h new file mode 100644 index 000000000..a63a33648 --- /dev/null +++ b/scintilla/deelx/deelx64.h @@ -0,0 +1,4830 @@ +// deelx64.h +// +// DEELX Regular Expression Engine (v1.3) +// +// Copyright 2006 ~ 2013 (c) RegExLab.com +// All Rights Reserved. +// +// http://www.regexlab.com/deelx/ +// +// Author: Ê·ÊÙΰ (sswater shi) +// sswater@gmail.com +// +// $Revision $ +// +// + adaption for 64-bit usage: "basetsd : INT_PTR" replaces int-pointer arithmetic and buffer indexes +// + Cppcheck cleanup + +#ifndef __DEELX_REGEXP64__H__ +#define __DEELX_REGEXP64__H__ + +#include +#include +#include +#include +#include + +#include + +namespace deelx +{ + // integer type for pointer arithmetic & casts (64-bit aware) + //typedef int index_t; // preserve original "deelx.h" v1.3 behavior + typedef INT_PTR index_t; + +extern "C" { + typedef int(*POSIX_FUNC)(int); + int isblank(int c); +} + +// +// Data Reference +// +template class CBufferRefT +{ +public: + explicit CBufferRefT(const ELT * pcsz, index_t length); + explicit CBufferRefT(const ELT * pcsz); + +public: + int nCompare(const ELT * pcsz) const; + int nCompareNoCase(const ELT * pcsz) const; + int Compare(const ELT * pcsz) const; + int CompareNoCase(const ELT * pcsz) const; + int Compare(const CBufferRefT &) const; + int CompareNoCase(const CBufferRefT &) const; + + ELT At(index_t nIndex, ELT def = 0) const; + ELT operator [] (index_t nIndex) const; + + const ELT * GetBuffer() const; + index_t GetSize() const; + +public: + virtual ~CBufferRefT(); + + // Content +protected: + ELT * m_pBuffer; + index_t m_nSize; +}; + +// +// Implemenation +// +template CBufferRefT ::CBufferRefT(const ELT * pcsz, index_t length) + : m_pBuffer((ELT *)pcsz) + , m_nSize(length) +{ +} + +template CBufferRefT ::CBufferRefT(const ELT * pcsz) + : m_pBuffer((ELT *)pcsz) + , m_nSize(0) +{ + if (pcsz != 0) while (m_pBuffer[m_nSize] != 0) m_nSize++; +} + +template int CBufferRefT ::nCompare(const ELT * pcsz) const +{ + for (index_t i = 0; i < m_nSize; i++) + { + if (m_pBuffer[i] != pcsz[i]) + return m_pBuffer[i] - pcsz[i]; + } + return 0; +} + +template int CBufferRefT ::nCompareNoCase(const ELT * pcsz) const +{ + for (index_t i = 0; i < m_nSize; i++) + { + if (m_pBuffer[i] != pcsz[i]) + { + if (toupper((int)m_pBuffer[i]) != toupper((int)pcsz[i])) + return m_pBuffer[i] - pcsz[i]; + } + } + + return 0; +} + +template inline int CBufferRefT ::Compare(const ELT * pcsz) const +{ + return nCompare(pcsz) ? 1 : (int)pcsz[m_nSize]; +} + +template inline int CBufferRefT ::CompareNoCase(const ELT * pcsz) const +{ + return nCompareNoCase(pcsz) ? 1 : (int)pcsz[m_nSize]; +} + +template inline int CBufferRefT ::Compare(const CBufferRefT & cref) const +{ + return m_nSize == cref.m_nSize ? nCompare(cref.GetBuffer()) : 1; +} + +template inline int CBufferRefT ::CompareNoCase(const CBufferRefT & cref) const +{ + return m_nSize == cref.m_nSize ? nCompareNoCase(cref.GetBuffer()) : 1; +} + +template inline ELT CBufferRefT ::At(index_t nIndex, ELT def) const +{ + return nIndex >= m_nSize ? def : m_pBuffer[nIndex]; +} + +template inline ELT CBufferRefT :: operator [] (index_t nIndex) const +{ + return nIndex >= m_nSize ? 0 : m_pBuffer[nIndex]; +} + +template const ELT * CBufferRefT ::GetBuffer() const +{ + static const ELT _def[] = { 0 }; return m_pBuffer ? m_pBuffer : _def; +} + +template inline index_t CBufferRefT ::GetSize() const +{ + return m_nSize; +} + +template CBufferRefT :: ~CBufferRefT() +{} + +// +// Data Buffer +// +template class CBufferT : public CBufferRefT +{ +public: + explicit CBufferT(const ELT * pcsz, index_t length); + explicit CBufferT(const ELT * pcsz); + CBufferT(); + +public: + ELT & operator [] (index_t nIndex); + const ELT & operator [] (index_t nIndex) const; + void Append(const ELT * pcsz, index_t length, index_t eol = 0); + void Append(ELT el, index_t eol = 0); + +public: + void Push(ELT el); + void Push(const CBufferRefT & buf); + int Pop(ELT & el); + int Pop(CBufferT & buf); + int Peek(ELT & el) const; + +public: + const ELT * GetBuffer() const; + ELT * GetBuffer(); + ELT * Detach(); + void Release(); + void Prepare(index_t index, int fill = 0); + void Restore(index_t size); + + ELT * PrepareInsert(index_t nPos, index_t nSize) + { + index_t nOldSize = CBufferRefT::m_nSize; + Restore(nPos > CBufferRefT::m_nSize ? nPos : CBufferRefT::m_nSize + nSize); + + if (nPos < nOldSize) + { + ELT * from = CBufferRefT::m_pBuffer + nPos, *to = CBufferRefT::m_pBuffer + nPos + nSize; + memmove(to, from, sizeof(ELT) * (nOldSize - nPos)); + } + + return CBufferRefT::m_pBuffer + nPos; + } + + void Insert(index_t nIndex, const ELT & rT) + { + Insert(nIndex, &rT, 1); + } + + void Insert(index_t nIndex, const ELT * pT, index_t nSize) + { + memcpy(PrepareInsert(nIndex, nSize), pT, sizeof(ELT) * nSize); + } + + void Remove(index_t nIndex) + { + Remove(nIndex, 1); + } + + void Remove(index_t nIndex, index_t nSize) + { + if (nIndex < CBufferRefT ::m_nSize) + { + if (nIndex + nSize >= CBufferRefT ::m_nSize) + { + Restore(nIndex); + } + else + { + memmove(CBufferRefT ::m_pBuffer + nIndex, CBufferRefT ::m_pBuffer + nIndex + nSize, sizeof(ELT) * (CBufferRefT ::m_nSize - nIndex - nSize)); + Restore(CBufferRefT ::m_nSize - nSize); + } + } + } + + void SetMaxLength(index_t nSize) + { + if (nSize > m_nMaxLength) + { + if (m_nMaxLength < 8) + m_nMaxLength = 8; + + if (nSize > m_nMaxLength) + m_nMaxLength *= 2; + + if (nSize > m_nMaxLength) + { + m_nMaxLength = nSize + 11; + m_nMaxLength -= m_nMaxLength & 0x07; + } + + CBufferRefT ::m_pBuffer = (ELT *)realloc(CBufferRefT ::m_pBuffer, sizeof(ELT) * m_nMaxLength); + } + } + +public: + virtual ~CBufferT(); + + // Content +protected: + index_t m_nMaxLength; +}; + +// +// Implemenation +// +template CBufferT ::CBufferT(const ELT * pcsz, index_t length) : CBufferRefT (0, length) +{ + m_nMaxLength = CBufferRefT ::m_nSize + 1; + + CBufferRefT ::m_pBuffer = (ELT *)malloc(sizeof(ELT) * m_nMaxLength); + memcpy(CBufferRefT::m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT ::m_nSize); + CBufferRefT::m_pBuffer[CBufferRefT ::m_nSize] = 0; +} + +template CBufferT ::CBufferT(const ELT * pcsz) : CBufferRefT (pcsz) +{ + m_nMaxLength = CBufferRefT ::m_nSize + 1; + + CBufferRefT ::m_pBuffer = (ELT *)malloc(sizeof(ELT) * m_nMaxLength); + memcpy(CBufferRefT::m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT ::m_nSize); + CBufferRefT::m_pBuffer[CBufferRefT ::m_nSize] = 0; +} + +template CBufferT ::CBufferT() : CBufferRefT (0, 0) +{ + m_nMaxLength = 0; + CBufferRefT::m_pBuffer = 0; +} + +template inline ELT & CBufferT :: operator [] (index_t nIndex) +{ + return CBufferRefT::m_pBuffer[nIndex]; +} + +template inline const ELT & CBufferT :: operator [] (index_t nIndex) const +{ + return CBufferRefT::m_pBuffer[nIndex]; +} + +template void CBufferT ::Append(const ELT * pcsz, index_t length, index_t eol) +{ + index_t nNewLength = m_nMaxLength; + + // Check length + if (nNewLength < 8) + nNewLength = 8; + + if (CBufferRefT ::m_nSize + length + eol > nNewLength) + nNewLength *= 2; + + if (CBufferRefT ::m_nSize + length + eol > nNewLength) + { + nNewLength = CBufferRefT ::m_nSize + length + eol + 11; + nNewLength -= nNewLength % 8; + } + + // Realloc + if (nNewLength > m_nMaxLength) + { + CBufferRefT ::m_pBuffer = (ELT *)realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // Append + memcpy(CBufferRefT::m_pBuffer + CBufferRefT ::m_nSize, pcsz, sizeof(ELT) * length); + CBufferRefT ::m_nSize += length; + + if (eol > 0) CBufferRefT::m_pBuffer[CBufferRefT ::m_nSize] = 0; +} + +template inline void CBufferT ::Append(ELT el, index_t eol) +{ + Append(&el, 1, eol); +} + +template void CBufferT ::Push(ELT el) +{ + // Realloc + if (CBufferRefT ::m_nSize >= m_nMaxLength) + { + index_t nNewLength = m_nMaxLength * 2; + if (nNewLength < 8) nNewLength = 8; + + CBufferRefT ::m_pBuffer = (ELT *)realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // Append + CBufferRefT::m_pBuffer[CBufferRefT ::m_nSize++] = el; +} + +template void CBufferT ::Push(const CBufferRefT & buf) +{ + for (index_t i = 0; i < buf.GetSize(); i++) + { + Push(buf[i]); + } + + Push((ELT)buf.GetSize()); +} + +template inline int CBufferT ::Pop(ELT & el) +{ + if (CBufferRefT ::m_nSize > 0) + { + el = CBufferRefT::m_pBuffer[--CBufferRefT ::m_nSize]; + return 1; + } + else + { + return 0; + } +} + +template int CBufferT ::Pop(CBufferT & buf) +{ + index_t size; + int res = 1; + res = res && Pop(*(ELT*)&size); + buf.Restore(size); + + for (index_t i = size - 1; i >= 0; i--) + { + res = res && Pop(buf[i]); + } + + return res; +} + +template inline int CBufferT ::Peek(ELT & el) const +{ + if (CBufferRefT ::m_nSize > 0) + { + el = CBufferRefT::m_pBuffer[CBufferRefT ::m_nSize - 1]; + return 1; + } + else + { + return 0; + } +} + +template const ELT * CBufferT ::GetBuffer() const +{ + static const ELT _def[] = { 0 }; return CBufferRefT::m_pBuffer ? CBufferRefT::m_pBuffer : _def; +} + +template ELT * CBufferT ::GetBuffer() +{ + static const ELT _def[] = { 0 }; return CBufferRefT::m_pBuffer ? CBufferRefT::m_pBuffer : (ELT *)_def; +} + +template ELT * CBufferT ::Detach() +{ + ELT * pBuffer = CBufferRefT::m_pBuffer; + + CBufferRefT ::m_pBuffer = 0; + CBufferRefT ::m_nSize = m_nMaxLength = 0; + + return pBuffer; +} + +template void CBufferT ::Release() +{ + ELT * pBuffer = Detach(); + + if (pBuffer != 0) free(pBuffer); +} + +template void CBufferT ::Prepare(index_t index, int fill) +{ + index_t nNewSize = index + 1; + + // Realloc + if (nNewSize > m_nMaxLength) + { + index_t nNewLength = m_nMaxLength; + + if (nNewLength < 8) + nNewLength = 8; + + if (nNewSize > nNewLength) + nNewLength *= 2; + + if (nNewSize > nNewLength) + { + nNewLength = nNewSize + 11; + nNewLength -= nNewLength % 8; + } + + CBufferRefT ::m_pBuffer = (ELT *)realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // size + if (CBufferRefT ::m_nSize < nNewSize) + { + memset(CBufferRefT::m_pBuffer + CBufferRefT ::m_nSize, fill, sizeof(ELT) * (nNewSize - CBufferRefT ::m_nSize)); + CBufferRefT ::m_nSize = nNewSize; + } +} + +template inline void CBufferT ::Restore(index_t size) +{ + SetMaxLength(size); + CBufferRefT ::m_nSize = size; +} + +template CBufferT :: ~CBufferT() +{ + if (CBufferRefT::m_pBuffer != 0) free(CBufferRefT::m_pBuffer); +} + +template class CSortedBufferT : public CBufferT +{ +public: + explicit CSortedBufferT(int reverse = 0); + explicit CSortedBufferT(int(*)(const void *, const void *)); + +public: + void Add(const T & rT); + void Add(const T * pT, index_t nSize); + int Remove(const T & rT); + static void RemoveAll(); + + void SortFreeze() { m_bSortFreezed = 1; } + void SortUnFreeze(); + +public: + int Find(const T & rT, int(*compare)(const void *, const void *) = 0) { return FindAs(*(T*)&rT, compare); } + int FindAs(const T & rT, int(*)(const void *, const void *) = 0); + index_t GetSize() const { return CBufferRefT::m_nSize; } + T & operator [] (index_t nIndex) { return CBufferT :: operator [] (nIndex); } + +protected: + int(*m_fncompare)(const void *, const void *); + static int compareT(const void *, const void *); + static int compareReverseT(const void *, const void *); + + int m_bSortFreezed; +}; + +template CSortedBufferT ::CSortedBufferT(int reverse) +{ + m_fncompare = reverse ? compareReverseT : compareT; + m_bSortFreezed = 0; +} + +template CSortedBufferT ::CSortedBufferT(int(*compare)(const void *, const void *)) +{ + m_fncompare = compare; + m_bSortFreezed = 0; +} + +template void CSortedBufferT ::Add(const T & rT) +{ + if (m_bSortFreezed != 0) + { + CBufferT ::Append(rT); + return; + } + + index_t a = 0, b = CBufferRefT::m_nSize - 1, c = CBufferRefT::m_nSize / 2; + + while (a <= b) + { + int r = m_fncompare(&rT, &CBufferRefT::m_pBuffer[c]); + + if (r < 0) b = c - 1; + else if (r > 0) a = c + 1; + else break; + + c = (a + b + 1) / 2; + } + + CBufferT ::Insert(c, rT); +} + +template void CSortedBufferT ::Add(const T * pT, index_t nSize) +{ + CBufferT ::Append(pT, nSize); + + if (m_bSortFreezed == 0) + { + qsort(CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), m_fncompare); + } +} + +template int CSortedBufferT ::FindAs(const T & rT, int(*compare)(const void *, const void *)) +{ + const T * pT = (const T *)bsearch(&rT, CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), compare == 0 ? m_fncompare : compare); + + if (pT != NULL) + return static_cast(pT - CBufferRefT::m_pBuffer); //TODO: x64bit > 4GB ? + else + return -1; +} + +template int CSortedBufferT ::Remove(const T & rT) +{ + int pos = Find(rT); + if (pos >= 0) CBufferT ::Remove(pos); + return pos; +} + +template inline void CSortedBufferT ::RemoveAll() +{ + CBufferT::Restore(0); +} + +template void CSortedBufferT ::SortUnFreeze() +{ + if (m_bSortFreezed != 0) + { + m_bSortFreezed = 0; + qsort(CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), m_fncompare); + } +} + +template int CSortedBufferT ::compareT(const void * elem1, const void * elem2) +{ + if (*(const T *)elem1 == *(const T *)elem2) + return 0; + else if (*(const T *)elem1 < *(const T *)elem2) + return -1; + else + return 1; +} + +template int CSortedBufferT ::compareReverseT(const void * elem1, const void * elem2) +{ + if (*(const T *)elem1 == *(const T *)elem2) + return 0; + else if (*(const T *)elem1 > *(const T *)elem2) + return -1; + else + return 1; +} + +// +// Context +// +class CContext +{ +public: + CBufferT m_stack; + CBufferT m_capturestack, m_captureindex; + +public: + index_t m_nCurrentPos; + index_t m_nBeginPos; + index_t m_nLastBeginPos; + index_t m_nParenZindex; + index_t m_nCursiveLimit; + + void * m_pMatchString; + index_t m_pMatchStringLength; +}; + +class CContextShot +{ +public: + explicit CContextShot(CContext * pContext) + { + m_nCurrentPos = pContext->m_nCurrentPos; + nsize = pContext->m_stack.GetSize(); + ncsize = pContext->m_capturestack.GetSize(); + } + + void Restore(CContext * pContext) + { + pContext->m_stack.Restore(nsize); + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = m_nCurrentPos; + } + +public: + index_t m_nCurrentPos; + index_t nsize; + index_t ncsize; +}; + +// +// Interface +// +class ElxInterface +{ +public: + virtual int Match(CContext * pContext) const = 0; + virtual int MatchNext(CContext * pContext) const = 0; + +public: + virtual ~ElxInterface() {}; +}; + +// +// Alternative +// +template class CAlternativeElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CAlternativeElxT(); + +public: + CBufferT m_elxlist; +}; + +typedef CAlternativeElxT <0> CAlternativeElx; + +// +// Assert +// +template class CAssertElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CAssertElxT(ElxInterface * pelx, int byes = 1); + +public: + ElxInterface * m_pelx; + int m_byes; +}; + +typedef CAssertElxT <0> CAssertElx; + +// +// Back reference elx +// +template class CBackrefElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBackrefElxT(int nnumber, int brightleft, int bignorecase); + +public: + index_t m_nnumber; + int m_brightleft; + int m_bignorecase; + + CBufferT m_szNamed; +}; + +// +// Implementation +// +template CBackrefElxT ::CBackrefElxT(int nnumber, int brightleft, int bignorecase) +{ + m_nnumber = nnumber; + m_brightleft = brightleft; + m_bignorecase = bignorecase; +} + +template int CBackrefElxT ::Match(CContext * pContext) const +{ + // check number, for named + if (m_nnumber < 0 || m_nnumber >= pContext->m_captureindex.GetSize()) return 0; + + index_t index = pContext->m_captureindex[m_nnumber]; + if (index < 0) return 0; + + // check enclosed + index_t pos1 = pContext->m_capturestack[index + 1]; + index_t pos2 = pContext->m_capturestack[index + 2]; + + if (pos2 < 0) pos2 = pContext->m_nCurrentPos; + + // info + index_t lpos = pos1 < pos2 ? pos1 : pos2; + index_t rpos = pos1 < pos2 ? pos2 : pos1; + index_t slen = rpos - lpos; + + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + index_t npos = pContext->m_nCurrentPos; + index_t tlen = pContext->m_pMatchStringLength; + + // compare + int bsucc; + CBufferRefT refstr(pcsz + lpos, slen); + + if (m_brightleft) + { + if (npos < slen) + return 0; + + if (m_bignorecase) + bsucc = !refstr.nCompareNoCase(pcsz + (npos - slen)); + else + bsucc = !refstr.nCompare(pcsz + (npos - slen)); + + if (bsucc) + { + pContext->m_stack.Push(npos); + pContext->m_nCurrentPos -= slen; + } + } + else + { + if (npos + slen > tlen) + return 0; + + if (m_bignorecase) + bsucc = !refstr.nCompareNoCase(pcsz + npos); + else + bsucc = !refstr.nCompare(pcsz + npos); + + if (bsucc) + { + pContext->m_stack.Push(npos); + pContext->m_nCurrentPos += slen; + } + } + + return bsucc; +} + +template int CBackrefElxT ::MatchNext(CContext * pContext) const +{ + index_t npos = 0; + + pContext->m_stack.Pop(npos); + pContext->m_nCurrentPos = npos; + + return 0; +} + +// RCHART +#ifndef RCHART +#define RCHART(ch) ((CHART)ch) +#endif + +// BOUNDARY_TYPE +enum BOUNDARY_TYPE +{ + BOUNDARY_FILE_BEGIN, // begin of whole text + BOUNDARY_FILE_END, // end of whole text + BOUNDARY_FILE_END_N, // end of whole text, or before newline at the end + BOUNDARY_LINE_BEGIN, // begin of line + BOUNDARY_LINE_END, // end of line + BOUNDARY_WORD_BEGIN, // begin of word + BOUNDARY_WORD_END, // end of word + BOUNDARY_WORD_EDGE +}; + +// +// Boundary Elx +// +template class CBoundaryElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBoundaryElxT(int ntype, int byes = 1); + +protected: + static int IsWordChar(CHART ch); + +public: + int m_ntype; + int m_byes; +}; + +// +// Implementation +// +template CBoundaryElxT ::CBoundaryElxT(int ntype, int byes) +{ + m_ntype = ntype; + m_byes = byes; +} + +template int CBoundaryElxT ::Match(CContext * pContext) const +{ + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + index_t npos = pContext->m_nCurrentPos; + index_t tlen = pContext->m_pMatchStringLength; + + CHART chL = npos > 0 ? pcsz[npos - 1] : 0; + CHART chR = npos < tlen ? pcsz[npos] : 0; + + int bsucc = 0; + + switch (m_ntype) + { + case BOUNDARY_FILE_BEGIN: + bsucc = (npos <= 0); + break; + + case BOUNDARY_FILE_END: + bsucc = (npos >= tlen); + break; + + case BOUNDARY_FILE_END_N: + bsucc = (npos >= tlen) || (pcsz[tlen - 1] == RCHART('\n') && (npos == tlen - 1 || (pcsz[tlen - 2] == RCHART('\r') && npos == tlen - 2))); + break; + + case BOUNDARY_LINE_BEGIN: + bsucc = (npos <= 0) || (chL == RCHART('\n')) || ((chL == RCHART('\r')) && (chR != RCHART('\n'))); + break; + + case BOUNDARY_LINE_END: + bsucc = (npos >= tlen) || (chR == RCHART('\r')) || ((chR == RCHART('\n')) && (chL != RCHART('\r'))); + break; + + case BOUNDARY_WORD_BEGIN: + bsucc = !IsWordChar(chL) && IsWordChar(chR); + break; + + case BOUNDARY_WORD_END: + bsucc = IsWordChar(chL) && !IsWordChar(chR); + break; + + case BOUNDARY_WORD_EDGE: + bsucc = IsWordChar(chL) ? !IsWordChar(chR) : IsWordChar(chR); + break; + } + + return m_byes ? bsucc : !bsucc; +} + +template int CBoundaryElxT ::MatchNext(CContext *) const +{ + return 0; +} + +template inline int CBoundaryElxT ::IsWordChar(CHART ch) +{ + return (ch >= RCHART('A') && ch <= RCHART('Z')) || (ch >= RCHART('a') && ch <= RCHART('z')) || (ch >= RCHART('0') && ch <= RCHART('9')) || (ch == RCHART('_')); +} + +// +// Bracket +// +template class CBracketElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBracketElxT(index_t nnumber, int bright); + static int CheckCaptureIndex(index_t & index, CContext * pContext, index_t number); + +public: + index_t m_nnumber; + index_t m_balancing; + int m_bright; + + CBufferT m_szNamed; + CBufferT m_szBalancing; +}; + +template CBracketElxT ::CBracketElxT(index_t nnumber, int bright) +{ + m_nnumber = nnumber; + m_bright = bright; + m_balancing = -1; +} + +template inline int CBracketElxT ::CheckCaptureIndex(index_t & index, CContext * pContext, index_t number) +{ + if (index >= pContext->m_capturestack.GetSize()) + index = pContext->m_capturestack.GetSize() - 4; + + while (index >= 0) + { + if (pContext->m_capturestack[index] == number) + { + return 1; + } + + index -= 4; + } + return 0; +} + +// +// capturestack[index+0] => Group number +// capturestack[index+1] => Capture start pos +// capturestack[index+2] => Capture end pos +// capturestack[index+3] => Capture enclose z-index, zindex<0 means inner group with same name +// +template int CBracketElxT ::Match(CContext * pContext) const +{ + // check, for named + if (m_nnumber < 0) return 0; + + if (!m_bright) + { + pContext->m_captureindex.Prepare(m_nnumber, -1); + index_t index = pContext->m_captureindex[m_nnumber]; + + // check + if (CheckCaptureIndex(index, pContext, m_nnumber) && pContext->m_capturestack[index + 2] < 0) + { + pContext->m_capturestack[index + 3] --; + return 1; + } + + // balancing left + if (m_balancing >= 0) + { + index_t balancing_index = pContext->m_captureindex[m_balancing]; + if (!CheckCaptureIndex(balancing_index, pContext, m_balancing) || + pContext->m_capturestack[balancing_index + 2] < 0) + { + return 0; + } + } + + // save + pContext->m_captureindex[m_nnumber] = pContext->m_capturestack.GetSize(); + + pContext->m_capturestack.Push(m_nnumber); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(0); // z-index + } + else + { + // check + index_t index = pContext->m_captureindex[m_nnumber]; + + if (CheckCaptureIndex(index, pContext, m_nnumber)) + { + if (pContext->m_capturestack[index + 3] < 0) // check inner group with same name + { + pContext->m_capturestack[index + 3] ++; + return 1; + } + + // balancing right + index_t balancing_index = -1; + if (m_balancing >= 0) + { + balancing_index = pContext->m_captureindex[m_balancing]; + if (!CheckCaptureIndex(balancing_index, pContext, m_balancing)) + { + // TODO ERROR + return 0; + } + } + + // save + pContext->m_capturestack[index + 2] = pContext->m_nCurrentPos; + pContext->m_capturestack[index + 3] = pContext->m_nParenZindex++; + + // balancing right + if (m_balancing >= 0) + { + // backup index + pContext->m_stack.Push(balancing_index); + + if (balancing_index >= 0) + { + pContext->m_capturestack[index + 2] = pContext->m_capturestack[index + 1]; + pContext->m_capturestack[index + 1] = pContext->m_capturestack[balancing_index + 2]; + + // destopy capture + pContext->m_capturestack[balancing_index] = -1; + balancing_index -= 4; + CheckCaptureIndex(balancing_index, pContext, m_balancing); + pContext->m_captureindex[m_balancing] = balancing_index; + } + } + } + } + + return 1; +} + +template int CBracketElxT ::MatchNext(CContext * pContext) const +{ + index_t index = pContext->m_captureindex[m_nnumber]; + if (!CheckCaptureIndex(index, pContext, m_nnumber)) + { + return 0; + } + + if (!m_bright) + { + if (pContext->m_capturestack[index + 3] < 0) + { + pContext->m_capturestack[index + 3] ++; + return 0; + } + + pContext->m_capturestack.Restore(pContext->m_capturestack.GetSize() - 4); + + // to find + CheckCaptureIndex(index, pContext, m_nnumber); + + // new index + pContext->m_captureindex[m_nnumber] = index; + } + else + { + if (pContext->m_capturestack[index + 2] >= 0) + { + // balancing right + if (m_balancing >= 0) + { + index_t balancing_index = -1; + pContext->m_stack.Pop(balancing_index); + + if (balancing_index >= 0) + { + pContext->m_capturestack[balancing_index] = m_balancing; + pContext->m_captureindex[m_balancing] = balancing_index; + } + } + pContext->m_capturestack[index + 2] = -1; + pContext->m_capturestack[index + 3] = 0; + } + else + { + pContext->m_capturestack[index + 3] --; + } + } + + return 0; +} + +// +// Deletage +// +template class CDelegateElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + explicit CDelegateElxT(int ndata = 0); + +public: + ElxInterface * m_pelx; + index_t m_ndata; // +0 : recursive to + // -3 : named recursive + + CBufferT m_szNamed; +}; + +template CDelegateElxT ::CDelegateElxT(int ndata) +{ + m_pelx = 0; + m_ndata = ndata; +} + +template int CDelegateElxT ::Match(CContext * pContext) const +{ + if (m_pelx != 0) + { + if (pContext->m_nCursiveLimit > 0) + { + pContext->m_nCursiveLimit--; + int result = m_pelx->Match(pContext); + pContext->m_nCursiveLimit++; + return result; + } + else + return 0; + } + else + return 1; +} + +template int CDelegateElxT ::MatchNext(CContext * pContext) const +{ + if (m_pelx != 0) + return m_pelx->MatchNext(pContext); + else + return 0; +} + +// +// Empty +// +template class CEmptyElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CEmptyElxT(); +}; + +typedef CEmptyElxT <0> CEmptyElx; + +// +// Global +// +template class CGlobalElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CGlobalElxT(); +}; + +typedef CGlobalElxT <0> CGlobalElx; + +// +// Repeat +// +template class CRepeatElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CRepeatElxT(ElxInterface * pelx, int ntimes); + +protected: + int MatchFixed(CContext * pContext) const; + int MatchNextFixed(CContext * pContext) const; + int MatchForward(CContext * pContext) const + { + CContextShot shot(pContext); + + if (!m_pelx->Match(pContext)) + return 0; + + if (pContext->m_nCurrentPos != shot.m_nCurrentPos) + return 1; + + if (!m_pelx->MatchNext(pContext)) + return 0; + + if (pContext->m_nCurrentPos != shot.m_nCurrentPos) + return 1; + + shot.Restore(pContext); + return 0; + } + +public: + ElxInterface * m_pelx; + int m_nfixed; +}; + +typedef CRepeatElxT <0> CRepeatElx; + +// +// Greedy +// +template class CGreedyElxT : public CRepeatElxT +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CGreedyElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); + +protected: + int MatchVart(CContext * pContext) const; + int MatchNextVart(CContext * pContext) const; + +public: + int m_nvart; +}; + +typedef CGreedyElxT <0> CGreedyElx; + +// +// Independent +// +template class CIndependentElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + explicit CIndependentElxT(ElxInterface * pelx); + +public: + ElxInterface * m_pelx; +}; + +typedef CIndependentElxT <0> CIndependentElx; + +// +// List +// +template class CListElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + explicit CListElxT(int brightleft); + +public: + CBufferT m_elxlist; + int m_brightleft; +}; + +typedef CListElxT <0> CListElx; + +// +// Posix Elx +// +template class CPosixElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CPosixElxT(const char * posix, int brightleft); + +public: + POSIX_FUNC m_posixfun; + int m_brightleft; + int m_byes; +}; + +// +// Implementation +// +template CPosixElxT ::CPosixElxT(const char * posix, int brightleft) +{ + m_brightleft = brightleft; + + if (posix[1] == '^') + { + m_byes = 0; + posix += 2; + } + else + { + m_byes = 1; + posix += 1; + } + + if (!strncmp(posix, "alnum:", 6)) m_posixfun = ::isalnum; + else if (!strncmp(posix, "alpha:", 6)) m_posixfun = ::isalpha; + else if (!strncmp(posix, "ascii:", 6)) m_posixfun = ::isascii; + else if (!strncmp(posix, "cntrl:", 6)) m_posixfun = ::iscntrl; + else if (!strncmp(posix, "digit:", 6)) m_posixfun = ::isdigit; + else if (!strncmp(posix, "graph:", 6)) m_posixfun = ::isgraph; + else if (!strncmp(posix, "lower:", 6)) m_posixfun = ::islower; + else if (!strncmp(posix, "print:", 6)) m_posixfun = ::isprint; + else if (!strncmp(posix, "punct:", 6)) m_posixfun = ::ispunct; + else if (!strncmp(posix, "space:", 6)) m_posixfun = ::isspace; + else if (!strncmp(posix, "upper:", 6)) m_posixfun = ::isupper; + else if (!strncmp(posix, "xdigit:", 7)) m_posixfun = ::isxdigit; + else if (!strncmp(posix, "blank:", 6)) m_posixfun = isblank; + else m_posixfun = 0; +} + +inline int isblank(int c) +{ + return c == 0x20 || c == '\t'; +} + +template int CPosixElxT ::Match(CContext * pContext) const +{ + if (m_posixfun == 0) return 0; + + index_t tlen = pContext->m_pMatchStringLength; + index_t npos = pContext->m_nCurrentPos; + + // check + index_t at = m_brightleft ? npos - 1 : npos; + if (at < 0 || at >= tlen) + return 0; + + CHART ch = ((const CHART *)pContext->m_pMatchString)[at]; + + int bsucc = (*m_posixfun)(ch); + + if (!m_byes) + bsucc = !bsucc; + + if (bsucc) + pContext->m_nCurrentPos += m_brightleft ? -1 : 1; + + return bsucc; +} + +template int CPosixElxT ::MatchNext(CContext * pContext) const +{ + pContext->m_nCurrentPos -= m_brightleft ? -1 : 1; + return 0; +} + +// +// Possessive +// +template class CPossessiveElxT : public CGreedyElxT +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CPossessiveElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); +}; + +typedef CPossessiveElxT <0> CPossessiveElx; + +// +// Range Elx +// +template class CRangeElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CRangeElxT(int brightleft, int byes); + +public: + int IsContainChar(CHART ch) const; + +public: + CBufferT m_ranges; + CBufferT m_chars; + CBufferT m_embeds; + +public: + int m_brightleft; + int m_byes; +}; + +// +// Implementation +// +template CRangeElxT ::CRangeElxT(int brightleft, int byes) +{ + m_brightleft = brightleft; + m_byes = byes; +} + +template int CRangeElxT ::Match(CContext * pContext) const +{ + index_t tlen = pContext->m_pMatchStringLength; + index_t npos = pContext->m_nCurrentPos; + + // check + index_t at = m_brightleft ? npos - 1 : npos; + if (at < 0 || at >= tlen) + return 0; + + CHART ch = ((const CHART *)pContext->m_pMatchString)[at]; + int bsucc = 0, i; + + // compare + for (i = 0; !bsucc && i < m_ranges.GetSize(); i += 2) + { + if (m_ranges[i] <= ch && ch <= m_ranges[i + 1]) bsucc = 1; + } + + for (i = 0; !bsucc && i < m_chars.GetSize(); i++) + { + if (m_chars[i] == ch) bsucc = 1; + } + + for (i = 0; !bsucc && i < m_embeds.GetSize(); i++) + { + if (m_embeds[i]->Match(pContext)) + { + pContext->m_nCurrentPos = npos; + bsucc = 1; + } + } + + if (!m_byes) + bsucc = !bsucc; + + if (bsucc) + pContext->m_nCurrentPos += m_brightleft ? -1 : 1; + + return bsucc; +} + +template int CRangeElxT ::IsContainChar(CHART ch) const +{ + int bsucc = 0, i; + + // compare + for (i = 0; !bsucc && i < m_ranges.GetSize(); i += 2) + { + if (m_ranges[i] <= ch && ch <= m_ranges[i + 1]) bsucc = 1; + } + + for (i = 0; !bsucc && i < m_chars.GetSize(); i++) + { + if (m_chars[i] == ch) bsucc = 1; + } + + return bsucc; +} + +template int CRangeElxT ::MatchNext(CContext * pContext) const +{ + pContext->m_nCurrentPos -= m_brightleft ? -1 : 1; + return 0; +} + +// +// Reluctant +// +template class CReluctantElxT : public CRepeatElxT +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CReluctantElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); + +protected: + static int MatchVart(CContext * pContext); + int MatchNextVart(CContext * pContext) const; + +public: + int m_nvart; +}; + +typedef CReluctantElxT <0> CReluctantElx; + +// +// String Elx +// +template class CStringElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CStringElxT(const CHART * fixed, index_t nlength, int brightleft, int bignorecase); + +public: + CBufferT m_szPattern; + int m_brightleft; + int m_bignorecase; +}; + +// +// Implementation +// +template CStringElxT ::CStringElxT(const CHART * fixed, index_t nlength, int brightleft, int bignorecase) : m_szPattern(fixed, nlength) +{ + m_brightleft = brightleft; + m_bignorecase = bignorecase; +} + +template int CStringElxT ::Match(CContext * pContext) const +{ + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + index_t npos = pContext->m_nCurrentPos; + index_t tlen = pContext->m_pMatchStringLength; + index_t slen = m_szPattern.GetSize(); + + int bsucc; + + if (m_brightleft) + { + if (npos < slen) + return 0; + + if (m_bignorecase) + bsucc = !m_szPattern.nCompareNoCase(pcsz + (npos - slen)); + else + bsucc = !m_szPattern.nCompare(pcsz + (npos - slen)); + + if (bsucc) + pContext->m_nCurrentPos -= slen; + } + else + { + if (npos + slen > tlen) + return 0; + + if (m_bignorecase) + bsucc = !m_szPattern.nCompareNoCase(pcsz + npos); + else + bsucc = !m_szPattern.nCompare(pcsz + npos); + + if (bsucc) + pContext->m_nCurrentPos += slen; + } + + return bsucc; +} + +template int CStringElxT ::MatchNext(CContext * pContext) const +{ + index_t slen = m_szPattern.GetSize(); + + if (m_brightleft) + pContext->m_nCurrentPos += slen; + else + pContext->m_nCurrentPos -= slen; + + return 0; +} + +// +// CConditionElx +// +template class CConditionElxT : public ElxInterface +{ +public: + int Match(CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CConditionElxT(); + +public: + // backref condition + index_t m_nnumber; + CBufferT m_szNamed; + + // elx condition + ElxInterface * m_pelxask; + + // selection + ElxInterface * m_pelxyes, *m_pelxno; +}; + +template CConditionElxT ::CConditionElxT() + : m_nnumber(-1) + , m_szNamed() + , m_pelxask(nullptr) + , m_pelxyes(nullptr) + , m_pelxno(nullptr) +{ +} + +template int CConditionElxT ::Match(CContext * pContext) const +{ + // status + index_t nbegin = pContext->m_nCurrentPos; + index_t nsize = pContext->m_stack.GetSize(); + index_t ncsize = pContext->m_capturestack.GetSize(); + + // condition result + int condition_yes = 0; + + // backref type + if (m_nnumber >= 0) + { + do + { + if (m_nnumber >= pContext->m_captureindex.GetSize()) break; + + index_t index = pContext->m_captureindex[m_nnumber]; + if (index < 0) break; + + // else valid + condition_yes = 1; + } while (0); + } + else + { + if (m_pelxask == 0) + condition_yes = 1; + else + condition_yes = m_pelxask->Match(pContext); + + pContext->m_stack.Restore(nsize); + pContext->m_nCurrentPos = nbegin; + } + + // elx result + int bsucc; + if (condition_yes) + bsucc = m_pelxyes == 0 ? 1 : m_pelxyes->Match(pContext); + else + bsucc = m_pelxno == 0 ? 1 : m_pelxno->Match(pContext); + + if (bsucc) + { + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(condition_yes); + } + else + { + pContext->m_capturestack.Restore(ncsize); + } + + return bsucc; +} + +template int CConditionElxT ::MatchNext(CContext * pContext) const +{ + // pop + index_t ncsize, condition_yes; + + pContext->m_stack.Pop(condition_yes); + pContext->m_stack.Pop(ncsize); + + // elx result + int bsucc; + if (condition_yes) + bsucc = m_pelxyes == 0 ? 0 : m_pelxyes->MatchNext(pContext); + else + bsucc = m_pelxno == 0 ? 0 : m_pelxno->MatchNext(pContext); + + if (bsucc) + { + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(condition_yes); + } + else + { + pContext->m_capturestack.Restore(ncsize); + } + + return bsucc; +} + +// +// MatchResult +// +template class MatchResultT +{ +public: + int IsMatched() const; + +public: + index_t GetStart() const; + index_t GetEnd() const; + +public: + index_t MaxGroupNumber() const; + index_t GetGroupStart(index_t nGroupNumber) const; + index_t GetGroupEnd(index_t nGroupNumber) const; + +public: + MatchResultT(const MatchResultT & from) { *this = from; } + MatchResultT(CContext * pContext = 0, index_t nMaxNumber = -1); + MatchResultT & operator = (const MatchResultT &); + inline operator int() const { return IsMatched(); } + +public: + CBufferT m_result; +}; + +typedef MatchResultT <0> MatchResult; + +// Stocked Elx IDs +enum STOCKELX_ID_DEFINES +{ + STOCKELX_EMPTY = 0, + + /////////////////////// + + STOCKELX_DOT_ALL, + STOCKELX_DOT_NOT_ALL, + + STOCKELX_WORD, + STOCKELX_WORD_NOT, + + STOCKELX_SPACE, + STOCKELX_SPACE_NOT, + + STOCKELX_DIGITAL, + STOCKELX_DIGITAL_NOT, + + ////////////////////// + + STOCKELX_DOT_ALL_RIGHTLEFT, + STOCKELX_DOT_NOT_ALL_RIGHTLEFT, + + STOCKELX_WORD_RIGHTLEFT, + STOCKELX_WORD_RIGHTLEFT_NOT, + + STOCKELX_SPACE_RIGHTLEFT, + STOCKELX_SPACE_RIGHTLEFT_NOT, + + STOCKELX_DIGITAL_RIGHTLEFT, + STOCKELX_DIGITAL_RIGHTLEFT_NOT, + + ///////////////////// + + STOCKELX_COUNT +}; + +// REGEX_FLAGS +#ifndef _REGEX_FLAGS_DEFINED +enum REGEX_FLAGS +{ + NO_FLAG = 0, + SINGLELINE = 0x01, + MULTILINE = 0x02, + GLOBAL = 0x04, + IGNORECASE = 0x08, + RIGHTTOLEFT = 0x10, + EXTENDED = 0x20 +}; +#define _REGEX_FLAGS_DEFINED +#endif + +// +// Builder T +// +template class CBuilderT +{ +public: + typedef CDelegateElxT CDelegateElx; + typedef CBracketElxT CBracketElx; + typedef CBackrefElxT CBackrefElx; + typedef CConditionElxT CConditionElx; + + // Methods +public: + ElxInterface * Build(const CBufferRefT & pattern, int flags); + index_t GetNamedNumber(const CBufferRefT & named) const; + void Clear(); + +public: + CBuilderT(); + virtual ~CBuilderT(); + + // Public Attributes +public: + ElxInterface * m_pTopElx; + int m_nFlags; + index_t m_nMaxNumber; + index_t m_nNextNamed; + index_t m_nGroupCount; + int m_nNextBalancing; + + CBufferT m_objlist; + CBufferT m_grouplist; + CBufferT m_recursivelist; + CBufferT m_namedlist; + CBufferT m_namedbackreflist; + CBufferT m_namedconditionlist; + CBufferT m_purebalancinglist; + + // CHART_INFO +protected: + struct CHART_INFO + { + public: + CHART ch; + int type; + int pos; + int len; + + public: + CHART_INFO(CHART c, int t, int p = 0, int l = 0) { ch = c; type = t; pos = p; len = l; } + inline int operator == (const CHART_INFO & ci) const { return ch == ci.ch && type == ci.type; } + inline int operator != (const CHART_INFO & ci) const { return !operator == (ci); } + }; + +protected: + static unsigned int Hex2Int(const CHART * pcsz, int length, int & used); + static int ReadDec(char * & str, unsigned int & dec); + void MoveNext(); + int GetNext2(); + + ElxInterface * BuildAlternative(int vaflags); + ElxInterface * BuildList(int & flags); + ElxInterface * BuildRepeat(int & flags); + ElxInterface * BuildSimple(int & flags); + ElxInterface * BuildCharset(int & flags); + ElxInterface * BuildRecursive(int & flags); + ElxInterface * BuildBoundary(int & flags); + ElxInterface * BuildBackref(int & flags); + + ElxInterface * GetStockElx(int nStockId); + ElxInterface * Keep(ElxInterface * pElx); + + // Private Attributes +protected: + CBufferRefT m_pattern; + CHART_INFO prev, curr, next, nex2; + int m_nNextPos; + int m_nCharsetDepth; + int m_bQuoted; + POSIX_FUNC m_quote_fun; + + // Backup current pos + struct Snapshot + { + CHART_INFO prev, curr, next, nex2; + int m_nNextPos; + int m_nCharsetDepth; + int m_bQuoted; + POSIX_FUNC m_quote_fun; + Snapshot() : prev(0, 0), curr(0, 0), next(0, 0), nex2(0, 0) + , m_nNextPos(0), m_nCharsetDepth(0), m_bQuoted(0), m_quote_fun() + {} + }; + void Backup(Snapshot * pdata) { memcpy(pdata, &prev, sizeof(Snapshot)); } + void Restore(Snapshot * pdata) { memcpy(&prev, pdata, sizeof(Snapshot)); } + + ElxInterface * m_pStockElxs[STOCKELX_COUNT]; +}; + +// +// Implementation +// +template CBuilderT ::CBuilderT() + : m_nFlags(0) + , m_nNextBalancing(0) +// protected + , m_pattern(0, 0) + , prev(0, 0) + , curr(0, 0) + , next(0, 0) + , nex2(0, 0) + , m_nNextPos(0) + , m_nCharsetDepth(0) + , m_bQuoted(0) + , m_quote_fun() +{ + Clear(); +} + +template CBuilderT :: ~CBuilderT() +{ + Clear(); +} + +template index_t CBuilderT ::GetNamedNumber(const CBufferRefT & named) const +{ + for (int i = 0; i < m_namedlist.GetSize(); i++) + { + if (!((CBracketElx *)m_namedlist[i]->m_elxlist[0])->m_szNamed.CompareNoCase(named)) + return ((CBracketElx *)m_namedlist[i]->m_elxlist[0])->m_nnumber; + } + + return -3; +} + +template ElxInterface * CBuilderT ::Build(const CBufferRefT & pattern, int flags) +{ + // init + m_pattern = pattern; + m_nNextPos = 0; + m_nCharsetDepth = 0; + m_nMaxNumber = 0; + m_nNextNamed = 0; + m_nNextBalancing = 0; + m_nFlags = flags; + m_bQuoted = 0; + m_quote_fun = 0; + + m_grouplist.Restore(0); + m_recursivelist.Restore(0); + m_namedlist.Restore(0); + m_namedbackreflist.Restore(0); + m_namedconditionlist.Restore(0); + m_purebalancinglist.Restore(0); + + int i; + for (i = 0; i < 3; i++) MoveNext(); + + // build + m_pTopElx = BuildAlternative(flags); + + // group 0 + m_grouplist.Prepare(0); + m_grouplist[0] = m_pTopElx; + + // append named to unnamed + m_nGroupCount = m_grouplist.GetSize(); + + m_grouplist.Prepare(m_nMaxNumber + m_namedlist.GetSize()); + + for (i = 0; i < m_namedlist.GetSize(); i++) + { + CBracketElx * pleft = (CBracketElx *)m_namedlist[i]->m_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_namedlist[i]->m_elxlist[2]; + + // append + m_grouplist[m_nGroupCount++] = m_namedlist[i]; + + if (pleft->m_nnumber > 0) + continue; + + // same name + index_t find_same_name = GetNamedNumber(pleft->m_szNamed); + if (find_same_name >= 0) + { + pleft->m_nnumber = find_same_name; + pright->m_nnumber = find_same_name; + } + else + { + m_nMaxNumber++; + + pleft->m_nnumber = m_nMaxNumber; + pright->m_nnumber = m_nMaxNumber; + } + } + + for (i = 0; i < m_namedlist.GetSize(); i++) + { + CBracketElx * pleft = (CBracketElx *)m_namedlist[i]->m_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_namedlist[i]->m_elxlist[2]; + + // balancing + if (pleft->m_szBalancing.GetSize() > 0) + { + index_t balancing_to = GetNamedNumber(pleft->m_szBalancing); + if (balancing_to >= 0) + { + pleft->m_balancing = balancing_to; + pright->m_balancing = balancing_to; + } + else + { + //TODO: ERROR + } + } + } + + for (i = 1; i < m_nGroupCount; i++) + { + CBracketElx * pleft = (CBracketElx *)((CListElx*)m_grouplist[i])->m_elxlist[0]; + + if (pleft->m_nnumber > m_nMaxNumber) + m_nMaxNumber = pleft->m_nnumber; + } + + // pure balancing group + index_t nMaxNumber = m_nMaxNumber; + for (i = 0; i < m_purebalancinglist.GetSize(); i++) + { + CBracketElx * pleft = (CBracketElx *)m_purebalancinglist[i]->m_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_purebalancinglist[i]->m_elxlist[2]; + + nMaxNumber++; + + pleft->m_nnumber = nMaxNumber; + pright->m_nnumber = nMaxNumber; + + // balancing + if (pleft->m_szBalancing.GetSize() > 0) + { + index_t balancing_to = GetNamedNumber(pleft->m_szBalancing); + if (balancing_to >= 0) + { + pleft->m_balancing = balancing_to; + pright->m_balancing = balancing_to; + } + else + { + //TODO: ERROR + } + } + } + + // connect recursive + for (i = 0; i < m_recursivelist.GetSize(); i++) + { + if (m_recursivelist[i]->m_ndata == -3) + m_recursivelist[i]->m_ndata = GetNamedNumber(m_recursivelist[i]->m_szNamed); + + if (m_recursivelist[i]->m_ndata >= 0 && m_recursivelist[i]->m_ndata <= m_nMaxNumber) + { + if (m_recursivelist[i]->m_ndata == 0) + m_recursivelist[i]->m_pelx = m_pTopElx; + else for (int j = 1; j < m_grouplist.GetSize(); j++) + { + if (m_recursivelist[i]->m_ndata == ((CBracketElx *)((CListElx*)m_grouplist[j])->m_elxlist[0])->m_nnumber) + { + m_recursivelist[i]->m_pelx = m_grouplist[j]; + break; + } + } + } + } + + // named backref + for (i = 0; i < m_namedbackreflist.GetSize(); i++) + { + m_namedbackreflist[i]->m_nnumber = GetNamedNumber(m_namedbackreflist[i]->m_szNamed); + } + + // named condition + for (i = 0; i < m_namedconditionlist.GetSize(); i++) + { + index_t nn = GetNamedNumber(m_namedconditionlist[i]->m_szNamed); + if (nn >= 0) + { + m_namedconditionlist[i]->m_nnumber = nn; + m_namedconditionlist[i]->m_pelxask = 0; + } + } + + return m_pTopElx; +} + +template void CBuilderT ::Clear() +{ + for (int i = 0; i < m_objlist.GetSize(); i++) + { + delete m_objlist[i]; + } + + m_objlist.Restore(0); + m_pTopElx = 0; + m_nMaxNumber = 0; + + memset(m_pStockElxs, 0, sizeof(m_pStockElxs)); +} + +// +// hex to int +// +template unsigned int CBuilderT ::Hex2Int(const CHART * pcsz, int length, int & used) +{ + unsigned int result = 0; + int & i = used; + + for (i = 0; i < length; i++) + { + if (pcsz[i] >= RCHART('0') && pcsz[i] <= RCHART('9')) + result = (result << 4) + (pcsz[i] - RCHART('0')); + else if (pcsz[i] >= RCHART('A') && pcsz[i] <= RCHART('F')) + result = (result << 4) + (0x0A + (pcsz[i] - RCHART('A'))); + else if (pcsz[i] >= RCHART('a') && pcsz[i] <= RCHART('f')) + result = (result << 4) + (0x0A + (pcsz[i] - RCHART('a'))); + else + break; + } + + return result; +} + +template inline ElxInterface * CBuilderT ::Keep(ElxInterface * pelx) +{ + m_objlist.Push(pelx); + return pelx; +} + +template void CBuilderT ::MoveNext() +{ + // forwards + prev = curr; + curr = next; + next = nex2; + + // get nex2 + while (!GetNext2()) {}; +} + +template int CBuilderT ::GetNext2() +{ + // check length + if (m_nNextPos >= m_pattern.GetSize()) + { + nex2 = CHART_INFO(0, 1, m_nNextPos, 0); + return 1; + } + + int delta = 1; + CHART ch = m_pattern[m_nNextPos]; + + // if quoted + if (m_bQuoted) + { + if (ch == RCHART('\\')) + { + if (m_pattern[m_nNextPos + 1] == RCHART('E')) + { + m_quote_fun = 0; + m_bQuoted = 0; + m_nNextPos += 2; + return 0; + } + } + + if (m_quote_fun != 0) + nex2 = CHART_INFO((CHART)(*m_quote_fun)((int)ch), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + + m_nNextPos += delta; + + return 1; + } + + // common + switch (ch) + { + case RCHART('\\'): + { + CHART ch1 = m_pattern[m_nNextPos + 1]; + + // backref + if (ch1 >= RCHART('0') && ch1 <= RCHART('9')) + { + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + break; + } + + // escape + delta = 2; + + switch (ch1) + { + case RCHART('A'): + case RCHART('Z'): + case RCHART('z'): + case RCHART('w'): + case RCHART('W'): + case RCHART('s'): + case RCHART('S'): + case RCHART('B'): + case RCHART('d'): + case RCHART('D'): + case RCHART('k'): + case RCHART('g'): + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + + case RCHART('b'): + if (m_nCharsetDepth > 0) + nex2 = CHART_INFO('\b', 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + + /* + case RCHART('<'): + case RCHART('>'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + */ + + case RCHART('x'): + if (m_pattern[m_nNextPos + 2] != '{') + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 2, red); + + delta += red; + + if (red > 0) + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + + break; + } + + case RCHART('u'): + if (m_pattern[m_nNextPos + 2] != '{') + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 4, red); + + delta += red; + + if (red > 0) + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + } + else + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 3, sizeof(int) * 2, red); + + delta += red; + + while (m_nNextPos + delta < m_pattern.GetSize() && m_pattern.At(m_nNextPos + delta) != RCHART('}')) + delta++; + + delta++; // skip '}' + + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + } + break; + + case RCHART('a'): nex2 = CHART_INFO(RCHART('\a'), 0, m_nNextPos, delta); break; + case RCHART('f'): nex2 = CHART_INFO(RCHART('\f'), 0, m_nNextPos, delta); break; + case RCHART('n'): nex2 = CHART_INFO(RCHART('\n'), 0, m_nNextPos, delta); break; + case RCHART('r'): nex2 = CHART_INFO(RCHART('\r'), 0, m_nNextPos, delta); break; + case RCHART('t'): nex2 = CHART_INFO(RCHART('\t'), 0, m_nNextPos, delta); break; + case RCHART('v'): nex2 = CHART_INFO(RCHART('\v'), 0, m_nNextPos, delta); break; + case RCHART('e'): nex2 = CHART_INFO(RCHART(27), 0, m_nNextPos, delta); break; + + case RCHART('G'): // skip '\G' + if (m_nCharsetDepth > 0) + { + m_nNextPos += 2; + return 0; + } + else + { + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + } + + case RCHART('L'): + if (!m_quote_fun) m_quote_fun = ::tolower; + + case RCHART('U'): + if (!m_quote_fun) m_quote_fun = ::toupper; + + case RCHART('Q'): + { + m_bQuoted = 1; + m_nNextPos += 2; + return 0; + } + + case RCHART('E'): + { + m_quote_fun = 0; + m_bQuoted = 0; + m_nNextPos += 2; + return 0; + } + + case 0: + if (m_nNextPos + 1 >= m_pattern.GetSize()) + { + delta = 1; + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); // common '\0' char + break; + + default: + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + break; + } + } + break; + + case RCHART('*'): + case RCHART('+'): + case RCHART('?'): + case RCHART('.'): + case RCHART('{'): + case RCHART('}'): + case RCHART(')'): + case RCHART('|'): + case RCHART('$'): + if (m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + break; + + case RCHART('-'): + if (m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case RCHART('('): + { + CHART ch1 = m_pattern[m_nNextPos + 1]; + CHART ch2 = m_pattern[m_nNextPos + 2]; + + // skip remark + if (ch1 == RCHART('?') && ch2 == RCHART('#')) + { + m_nNextPos += 2; + while (m_nNextPos < m_pattern.GetSize()) + { + if (m_pattern[m_nNextPos] == RCHART(')')) + break; + + m_nNextPos++; + } + + if (m_pattern[m_nNextPos] == RCHART(')')) + { + m_nNextPos++; + + // get next nex2 + return 0; + } + } + else + { + if (m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + } + break; + + case RCHART('#'): + if (m_nFlags & EXTENDED) + { + // skip remark + m_nNextPos++; + + while (m_nNextPos < m_pattern.GetSize()) + { + if (m_pattern[m_nNextPos] == RCHART('\n') || m_pattern[m_nNextPos] == RCHART('\r')) + break; + + m_nNextPos++; + } + + // get next nex2 + return 0; + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(' '): + case RCHART('\f'): + case RCHART('\n'): + case RCHART('\r'): + case RCHART('\t'): + case RCHART('\v'): + if (m_nFlags & EXTENDED) + { + m_nNextPos++; + + // get next nex2 + return 0; + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART('['): + if (m_nCharsetDepth == 0 || m_pattern.At(m_nNextPos + 1, 0) == RCHART(':')) + { + m_nCharsetDepth++; + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(']'): + if (m_nCharsetDepth > 0) + { + m_nCharsetDepth--; + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(':'): + if (next == CHART_INFO(RCHART('['), 1)) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case RCHART('^'): + if (m_nCharsetDepth == 0 || next == CHART_INFO(RCHART('['), 1) || (curr == CHART_INFO(RCHART('['), 1) && next == CHART_INFO(RCHART(':'), 1))) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case 0: + if (m_nNextPos >= m_pattern.GetSize()) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); // end of string + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); // common '\0' char + break; + + default: + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + } + + m_nNextPos += delta; + + return 1; +} + +template ElxInterface * CBuilderT ::GetStockElx(int nStockId) +{ + ElxInterface ** pStockElxs = m_pStockElxs; + + // check + if (nStockId < 0 || nStockId >= STOCKELX_COUNT) + return GetStockElx(0); + + // create if no + if (pStockElxs[nStockId] == 0) + { + switch (nStockId) + { + case STOCKELX_EMPTY: + pStockElxs[nStockId] = Keep(new CEmptyElx()); + break; + + case STOCKELX_WORD: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars.Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars.Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DOT_ALL: + pStockElxs[nStockId] = Keep(new CRangeElxT (0, 0)); + break; + + case STOCKELX_DOT_NOT_ALL: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_chars.Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_chars.Push(RCHART(' ')); + pRange->m_chars.Push(RCHART('\t')); + pRange->m_chars.Push(RCHART('\r')); + pRange->m_chars.Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_chars.Push(RCHART(' ')); + pRange->m_chars.Push(RCHART('\t')); + pRange->m_chars.Push(RCHART('\r')); + pRange->m_chars.Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars.Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars.Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DOT_ALL_RIGHTLEFT: + pStockElxs[nStockId] = Keep(new CRangeElxT (1, 0)); + break; + + case STOCKELX_DOT_NOT_ALL_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_chars.Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_chars.Push(RCHART(' ')); + pRange->m_chars.Push(RCHART('\t')); + pRange->m_chars.Push(RCHART('\r')); + pRange->m_chars.Push(RCHART('\n')); + pRange->m_chars.Push(RCHART('\f')); + pRange->m_chars.Push(RCHART('\v')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_chars.Push(RCHART(' ')); + pRange->m_chars.Push(RCHART('\t')); + pRange->m_chars.Push(RCHART('\r')); + pRange->m_chars.Push(RCHART('\n')); + pRange->m_chars.Push(RCHART('\f')); + pRange->m_chars.Push(RCHART('\v')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + } + } + + // return + return pStockElxs[nStockId]; +} + +template ElxInterface * CBuilderT ::BuildAlternative(int vaflags) +{ + if (curr == CHART_INFO(0, 1)) + return GetStockElx(STOCKELX_EMPTY); + + // flag instance + int flags = vaflags; + + // first part + ElxInterface * pAlternativeOne = BuildList(flags); + + // check alternative + if (curr == CHART_INFO(RCHART('|'), 1)) + { + CAlternativeElx * pAlternative = (CAlternativeElx *)Keep(new CAlternativeElx()); + pAlternative->m_elxlist.Push(pAlternativeOne); + + // loop + while (curr == CHART_INFO(RCHART('|'), 1)) + { + // skip '|' itself + MoveNext(); + + pAlternativeOne = BuildList(flags); + pAlternative->m_elxlist.Push(pAlternativeOne); + } + + return pAlternative; + } + + return pAlternativeOne; +} + +template ElxInterface * CBuilderT ::BuildList(int & flags) +{ + if (curr == CHART_INFO(0, 1) || curr == CHART_INFO(RCHART('|'), 1) || curr == CHART_INFO(RCHART(')'), 1)) + return GetStockElx(STOCKELX_EMPTY); + + // first + ElxInterface * pListOne = BuildRepeat(flags); + + if (curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1)) + { + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + pList->m_elxlist.Push(pListOne); + + while (curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1)) + { + pListOne = BuildRepeat(flags); + + // add + pList->m_elxlist.Push(pListOne); + } + + return pList; + } + + return pListOne; +} + +template ElxInterface * CBuilderT ::BuildRepeat(int & flags) +{ + // simple + ElxInterface * pSimple = BuildSimple(flags); + + if (curr.type == 0) return pSimple; + + // is quantifier or not + int bIsQuantifier = 1; + + // quantifier range + unsigned int nMin = 0, nMax = 0; + + switch (curr.ch) + { + case RCHART('{'): + { + CBufferT re; + + // skip '{' + MoveNext(); + + // copy + while (curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('}'), 1)) + { + re.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + // skip '}' + MoveNext(); + + // read + int red; + char * str = re.GetBuffer(); + + if (!ReadDec(str, nMin)) + red = 0; + else if (*str != ',') + red = 1; + else + { + str++; + + if (!ReadDec(str, nMax)) + red = 2; + else + red = 3; + } + + // check + if (red <= 1) nMax = nMin; + if (red == 2) nMax = INT_MAX; + if (nMax < nMin) nMax = nMin; + } + break; + + case RCHART('?'): + nMin = 0; + nMax = 1; + + // skip '?' + MoveNext(); + break; + + case RCHART('*'): + nMin = 0; + nMax = INT_MAX; + + // skip '*' + MoveNext(); + break; + + case RCHART('+'): + nMin = 1; + nMax = INT_MAX; + + // skip '+' + MoveNext(); + break; + + default: + bIsQuantifier = 0; + break; + } + + // do quantify + if (bIsQuantifier) + { + // 0 times + if (nMax == 0) + return GetStockElx(STOCKELX_EMPTY); + + // fixed times + if (nMin == nMax) + { + if (curr == CHART_INFO(RCHART('?'), 1) || curr == CHART_INFO(RCHART('+'), 1)) + MoveNext(); + + return Keep(new CRepeatElx(pSimple, nMin)); + } + + // range times + if (curr == CHART_INFO(RCHART('?'), 1)) + { + MoveNext(); + return Keep(new CReluctantElx(pSimple, nMin, nMax)); + } + else if (curr == CHART_INFO(RCHART('+'), 1)) + { + MoveNext(); + return Keep(new CPossessiveElx(pSimple, nMin, nMax)); + } + else + { + return Keep(new CGreedyElx(pSimple, nMin, nMax)); + } + } + + return pSimple; +} + +template ElxInterface * CBuilderT ::BuildSimple(int & flags) +{ + CBufferT fixed; + + while (curr != CHART_INFO(0, 1)) + { + if (curr.type == 0) + { + if (next == CHART_INFO(RCHART('{'), 1) || next == CHART_INFO(RCHART('?'), 1) || next == CHART_INFO(RCHART('*'), 1) || next == CHART_INFO(RCHART('+'), 1)) + { + if (fixed.GetSize() == 0) + { + fixed.Append(curr.ch, 1); + MoveNext(); + } + + break; + } + else + { + fixed.Append(curr.ch, 1); + MoveNext(); + } + } + else if (curr.type == 1) + { + CHART vch = curr.ch; + + // end of simple + if (vch == RCHART(')') || vch == RCHART('|')) + break; + + // has fixed already + if (fixed.GetSize() > 0) + break; + + // left parentheses + if (vch == RCHART('(')) + { + return BuildRecursive(flags); + } + + // char set + if (vch == RCHART('[') || vch == RCHART('.') || vch == RCHART('w') || vch == RCHART('W') || + vch == RCHART('s') || vch == RCHART('S') || vch == RCHART('d') || vch == RCHART('D') + ) + { + return BuildCharset(flags); + } + + // boundary + if (vch == RCHART('^') || vch == RCHART('$') || vch == RCHART('A') || vch == RCHART('Z') || vch == RCHART('z') || + vch == RCHART('b') || vch == RCHART('B') || vch == RCHART('G') // vch == RCHART('<') || vch == RCHART('>') + ) + { + return BuildBoundary(flags); + } + + // backref + if (vch == RCHART('\\') || vch == RCHART('k') || vch == RCHART('g')) + { + return BuildBackref(flags); + } + + // treat vchar as char + fixed.Append(curr.ch, 1); + MoveNext(); + } + } + + if (fixed.GetSize() > 0) + return Keep(new CStringElxT (fixed.GetBuffer(), fixed.GetSize(), flags & RIGHTTOLEFT, flags & IGNORECASE)); + else + return GetStockElx(STOCKELX_EMPTY); +} + +#define deelx_max(a, b) (((a) > (b)) ? (a) : (b)) +#define deelx_min(a, b) (((a) < (b)) ? (a) : (b)) + +template ElxInterface * CBuilderT ::BuildCharset(int & flags) +{ + // char + CHART ch = curr.ch; + + // skip + MoveNext(); + + switch (ch) + { + case RCHART('.'): + return GetStockElx( + (flags & RIGHTTOLEFT) ? + ((flags & SINGLELINE) ? STOCKELX_DOT_ALL_RIGHTLEFT : STOCKELX_DOT_NOT_ALL_RIGHTLEFT) : + ((flags & SINGLELINE) ? STOCKELX_DOT_ALL : STOCKELX_DOT_NOT_ALL) + ); + + case RCHART('w'): + return GetStockElx((flags & RIGHTTOLEFT) ? STOCKELX_WORD_RIGHTLEFT : STOCKELX_WORD); + + case RCHART('W'): + return GetStockElx((flags & RIGHTTOLEFT) ? STOCKELX_WORD_RIGHTLEFT_NOT : STOCKELX_WORD_NOT); + + case RCHART('s'): + return GetStockElx((flags & RIGHTTOLEFT) ? STOCKELX_SPACE_RIGHTLEFT : STOCKELX_SPACE); + + case RCHART('S'): + return GetStockElx((flags & RIGHTTOLEFT) ? STOCKELX_SPACE_RIGHTLEFT_NOT : STOCKELX_SPACE_NOT); + + case RCHART('d'): + return GetStockElx((flags & RIGHTTOLEFT) ? STOCKELX_DIGITAL_RIGHTLEFT : STOCKELX_DIGITAL); + + case RCHART('D'): + return GetStockElx((flags & RIGHTTOLEFT) ? STOCKELX_DIGITAL_RIGHTLEFT_NOT : STOCKELX_DIGITAL_NOT); + + case RCHART('['): + { + CRangeElxT * pRange; + + // create + if (curr == CHART_INFO(RCHART(':'), 1)) + { + // Backup before posix + Snapshot shot; + Backup(&shot); + + CBufferT posix; + + do + { + posix.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } while (curr.ch != RCHART(0) && curr != CHART_INFO(RCHART(']'), 1)); + + MoveNext(); // skip ']' + + // posix + CPosixElxT * pposix = (CPosixElxT *) Keep(new CPosixElxT (posix.GetBuffer(), flags & RIGHTTOLEFT)); + if (pposix->m_posixfun != 0) + { + return pposix; + } + + // restore if not posix + Restore(&shot); + } + + if (curr == CHART_INFO(RCHART('^'), 1)) + { + MoveNext(); // skip '^' + pRange = (CRangeElxT *)Keep(new CRangeElxT (flags & RIGHTTOLEFT, 0)); + } + else + { + pRange = (CRangeElxT *)Keep(new CRangeElxT (flags & RIGHTTOLEFT, 1)); + } + + // parse + while (curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART(']'), 1)) + { + ch = curr.ch; + + if (curr.type == 1 && ( + ch == RCHART('.') || ch == RCHART('w') || ch == RCHART('W') || ch == RCHART('s') || ch == RCHART('S') || ch == RCHART('d') || ch == RCHART('D') || + (ch == RCHART('[') && next == CHART_INFO(RCHART(':'), 1)) + )) + { + pRange->m_embeds.Push(BuildCharset(flags)); + } + else if (next == CHART_INFO(RCHART('-'), 1) && nex2.type == 0) + { + pRange->m_ranges.Push(ch); pRange->m_ranges.Push(nex2.ch); + + // next + MoveNext(); + MoveNext(); + MoveNext(); + } + else + { + pRange->m_chars.Push(ch); + + // next + MoveNext(); + } + } + + // skip ']' + MoveNext(); + + if (flags & IGNORECASE) + { + CBufferT & ranges = pRange->m_ranges; + index_t i, oldcount = ranges.GetSize() / 2; + + for (i = 0; i < oldcount; i++) + { + CHART newmin, newmax; + + if (ranges[i * 2] <= RCHART('Z') && ranges[i * 2 + 1] >= RCHART('A')) + { + newmin = tolower(deelx_max(RCHART('A'), ranges[i * 2])); + newmax = tolower(deelx_min(RCHART('Z'), ranges[i * 2 + 1])); + + if (newmin < ranges[i * 2] || newmax > ranges[i * 2 + 1]) + { + ranges.Push(newmin); + ranges.Push(newmax); + } + } + + if (ranges[i * 2] <= RCHART('z') && ranges[i * 2 + 1] >= RCHART('a')) + { + newmin = toupper(deelx_max(RCHART('a'), ranges[i * 2])); + newmax = toupper(deelx_min(RCHART('z'), ranges[i * 2 + 1])); + + if (newmin < ranges[i * 2] || newmax > ranges[i * 2 + 1]) + { + ranges.Push(newmin); + ranges.Push(newmax); + } + } + } + + CBufferT & chars = pRange->m_chars; + oldcount = chars.GetSize(); + for (i = 0; i < oldcount; i++) + { + if (isupper(chars[i]) && !pRange->IsContainChar(tolower(chars[i]))) + chars.Push(tolower(chars[i])); + + if (islower(chars[i]) && !pRange->IsContainChar(toupper(chars[i]))) + chars.Push(toupper(chars[i])); + } + } + + return pRange; + } + } + + return GetStockElx(STOCKELX_EMPTY); +} + +template ElxInterface * CBuilderT ::BuildRecursive(int & flags) +{ + // skip '(' + MoveNext(); + + if (curr == CHART_INFO(RCHART('?'), 1)) + { + ElxInterface * pElx = 0; + + // skip '?' + MoveNext(); + + int bNegative = 0; + CHART named_end = RCHART('>'); + + switch (curr.ch) + { + case RCHART('!'): + bNegative = 1; + + case RCHART('='): + { + MoveNext(); // skip '!' or '=' + pElx = Keep(new CAssertElx(BuildAlternative(flags & ~RIGHTTOLEFT), !bNegative)); + } + break; + + case RCHART('<'): + switch (next.ch) + { + case RCHART('!'): + bNegative = 1; + + case RCHART('='): + MoveNext(); // skip '<' + MoveNext(); // skip '!' or '=' + { + pElx = Keep(new CAssertElx(BuildAlternative(flags | RIGHTTOLEFT), !bNegative)); + } + break; + + default: // named group + break; + } + // break if assertion // else named + if (pElx != 0) break; + + case RCHART('P'): + if (curr.ch == RCHART('P')) MoveNext(); // skip 'P' + + case RCHART('\''): + if (curr.ch == RCHART('<')) named_end = RCHART('>'); + else if (curr.ch == RCHART('\'')) named_end = RCHART('\''); + MoveNext(); // skip '<' or '\'' + { + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + CBracketElx * pleft = (CBracketElx *)Keep(new CBracketElx(-1, (flags & RIGHTTOLEFT) ? 1 : 0)); + CBracketElx * pright = (CBracketElx *)Keep(new CBracketElx(-1, (flags & RIGHTTOLEFT) ? 0 : 1)); + + // save name + CBufferT & name = pleft->m_szNamed, &balancing_name = pleft->m_szBalancing, *pname = &name; + CBufferT num, balancing_num, *pnum = # + + while (curr.ch != RCHART(0) && curr.ch != named_end) + { + if (curr.ch == RCHART('-')) + { + pname = &balancing_name; + pnum = &balancing_num; + MoveNext(); + continue; + } + + pname->Append(curr.ch, 1); + pnum->Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if (ReadDec(str, number) ? (*str == '\0') : 0) + { + pleft->m_nnumber = number; + pright->m_nnumber = number; + + name.Release(); + } + + str = balancing_num.GetBuffer(); + if (ReadDec(str, number) ? (*str == '\0') : 0) + { + pleft->m_balancing = number; + pright->m_balancing = number; + + balancing_name.Release(); + } + + // left, center, right + pList->m_elxlist.Push(pleft); + pList->m_elxlist.Push(BuildAlternative(flags)); + pList->m_elxlist.Push(pright); + + // named number + if (pleft->m_nnumber >= 0 || name.GetSize() > 0) + { + index_t nThisBackref = m_nNextNamed++; + m_namedlist.Prepare(nThisBackref); + m_namedlist[nThisBackref] = pList; + } + else if (pleft->m_balancing >= 0 || balancing_name.GetSize() > 0) + { + int nThisBalancing = m_nNextBalancing++; + m_purebalancinglist.Prepare(nThisBalancing, 0); + m_purebalancinglist[nThisBalancing] = pList; + } + else + { + // TODO ERROR + } + + pElx = pList; + } + break; + + case RCHART('>'): + { + MoveNext(); // skip '>' + pElx = Keep(new CIndependentElx(BuildAlternative(flags))); + } + break; + + case RCHART('R'): + MoveNext(); // skip 'R' + while (curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space + + if (curr.ch == RCHART('<') || curr.ch == RCHART('\'')) + { + named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\''); + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(-3)); + + MoveNext(); // skip '<' or '\\' + + // save name + CBufferT & name = pDelegate->m_szNamed; + CBufferT num; + + while (curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if (ReadDec(str, number) ? (*str == '\0') : 0) + { + pDelegate->m_ndata = number; + name.Release(); + } + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + else + { + CBufferT rto; + while (curr.ch != RCHART(0) && curr.ch != RCHART(')')) + { + rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + unsigned int rtono = 0; + char * str = rto.GetBuffer(); + ReadDec(str, rtono); + + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono)); + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + break; + + case RCHART('('): + { + CConditionElx * pConditionElx = (CConditionElx *)Keep(new CConditionElx()); + + // condition + ElxInterface * & pCondition = pConditionElx->m_pelxask; + + if (next == CHART_INFO(RCHART('?'), 1)) + { + pCondition = BuildRecursive(flags); + } + else // named, assert or number + { + MoveNext(); // skip '(' + int pos0 = curr.pos; + + // save elx condition + pCondition = Keep(new CAssertElx(BuildAlternative(flags), 1)); + + // save name + pConditionElx->m_szNamed.Append(m_pattern.GetBuffer() + pos0, curr.pos - pos0, 1); + + // save number + CBufferT numstr; + while (pos0 < curr.pos) + { + CHART ch = m_pattern[pos0]; + numstr.Append(((ch & (CHART)0xff) == ch) ? (char)ch : 0, 1); + pos0++; + } + + unsigned int number; + char * str = numstr.GetBuffer(); + + // valid group number + if (ReadDec(str, number) ? (*str == '\0') : 0) + { + pConditionElx->m_nnumber = number; + pCondition = 0; + } + else // maybe elx, maybe named + { + pConditionElx->m_nnumber = -1; + m_namedconditionlist.Push(pConditionElx); + } + + MoveNext(); // skip ')' + } + + // alternative + { + int newflags = flags; + + pConditionElx->m_pelxyes = BuildList(newflags); + } + + if (curr.ch == RCHART('|')) + { + MoveNext(); // skip '|' + + pConditionElx->m_pelxno = BuildAlternative(flags); + } + else + { + pConditionElx->m_pelxno = 0; + } + + pElx = pConditionElx; + } + break; + + default: + while (curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space + + if (curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) // recursive (?1) => (?R1) + { + CBufferT rto; + while (curr.ch != RCHART(0) && curr.ch != RCHART(')')) + { + rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + unsigned int rtono = 0; + char * str = rto.GetBuffer(); + ReadDec(str, rtono); + + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono)); + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + else + { + // flag + int newflags = flags; + while (curr != CHART_INFO(0, 1) && curr.ch != RCHART(':') && curr.ch != RCHART(')') && curr != CHART_INFO(RCHART('('), 1)) + { + int tochange = 0; + + switch (curr.ch) + { + case RCHART('i'): + case RCHART('I'): + tochange = IGNORECASE; + break; + + case RCHART('s'): + case RCHART('S'): + tochange = SINGLELINE; + break; + + case RCHART('m'): + case RCHART('M'): + tochange = MULTILINE; + break; + + case RCHART('g'): + case RCHART('G'): + tochange = GLOBAL; + break; + + case RCHART('-'): + bNegative = 1; + break; + } + + if (bNegative) + newflags &= ~tochange; + else + newflags |= tochange; + + // move to next char + MoveNext(); + } + + if (curr.ch == RCHART(':') || curr == CHART_INFO(RCHART('('), 1)) + { + // skip ':' + if (curr.ch == RCHART(':')) MoveNext(); + + pElx = BuildAlternative(newflags); + } + else + { + // change parent flags + flags = newflags; + + pElx = GetStockElx(STOCKELX_EMPTY); + } + } + break; + } + + MoveNext(); // skip ')' + + return pElx; + } + else + { + // group and number + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + index_t nThisBackref = ++m_nMaxNumber; + + // left, center, right + pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, (flags & RIGHTTOLEFT) ? 1 : 0))); + pList->m_elxlist.Push(BuildAlternative(flags)); + pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, (flags & RIGHTTOLEFT) ? 0 : 1))); + + // for recursive + m_grouplist.Prepare(nThisBackref); + m_grouplist[nThisBackref] = pList; + + // right + MoveNext(); // skip ')' + + return pList; + } +} + +template ElxInterface * CBuilderT ::BuildBoundary(int & flags) +{ + // char + CHART ch = curr.ch; + + // skip + MoveNext(); + + switch (ch) + { + case RCHART('^'): + return Keep(new CBoundaryElxT ((flags & MULTILINE) ? BOUNDARY_LINE_BEGIN : BOUNDARY_FILE_BEGIN)); + + case RCHART('$'): + return Keep(new CBoundaryElxT ((flags & MULTILINE) ? BOUNDARY_LINE_END : BOUNDARY_FILE_END)); + + case RCHART('b'): + return Keep(new CBoundaryElxT (BOUNDARY_WORD_EDGE)); + + case RCHART('B'): + return Keep(new CBoundaryElxT (BOUNDARY_WORD_EDGE, 0)); + + case RCHART('A'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_BEGIN)); + + case RCHART('Z'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_END_N)); + + case RCHART('z'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_END)); + + case RCHART('G'): + if (flags & GLOBAL) + return Keep(new CGlobalElx()); + else + return GetStockElx(STOCKELX_EMPTY); + + default: + return GetStockElx(STOCKELX_EMPTY); + } +} + +template ElxInterface * CBuilderT ::BuildBackref(int & flags) +{ + // skip '\\' or '\k' or '\g' + MoveNext(); + + if (curr.ch == RCHART('<') || curr.ch == RCHART('\'')) + { + CHART named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\''); + CBackrefElxT * pbackref = (CBackrefElxT *)Keep(new CBackrefElxT (-1, flags & RIGHTTOLEFT, flags & IGNORECASE)); + + MoveNext(); // skip '<' or '\'' + + // save name + CBufferT & name = pbackref->m_szNamed; + CBufferT num; + + while (curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if (ReadDec(str, number) ? (*str == '\0') : 0) + { + pbackref->m_nnumber = number; + name.Release(); + } + else + { + m_namedbackreflist.Push(pbackref); + } + + return pbackref; + } + else + { + unsigned int nbackref = 0; + + for (int i = 0; i < 3; i++) + { + if (curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) + nbackref = nbackref * 10 + (curr.ch - RCHART('0')); + else + break; + + MoveNext(); + } + + return Keep(new CBackrefElxT (nbackref, flags & RIGHTTOLEFT, flags & IGNORECASE)); + } +} + +template int CBuilderT ::ReadDec(char * & str, unsigned int & dec) +{ + int s = 0; + while (str[s] != 0 && isspace(str[s])) s++; + + if (str[s] < '0' || str[s] > '9') return 0; + + dec = 0; + unsigned int i; + + for (i = s; i < sizeof(CHART) * 3 + s; i++) + { + if (str[i] >= '0' && str[i] <= '9') + dec = dec * 10 + (str[i] - '0'); + else + break; + } + + while (str[i] != 0 && isspace(str[i])) i++; + str += i; + + return 1; +} + +// +// Regexp +// +template class CRegexpT +{ +public: + CRegexpT(const CHART * pattern = 0, int flags = 0); + CRegexpT(const CHART * pattern, index_t length, int flags); + void Compile(const CHART * pattern, int flags = 0); + void Compile(const CHART * pattern, index_t length, int flags); + +public: + MatchResult MatchExact(const CHART * tstring, CContext * pContext = 0) const; + MatchResult MatchExact(const CHART * tstring, int length, CContext * pContext = 0) const; + MatchResult Match(const CHART * tstring, int start = -1, CContext * pContext = 0) const; + MatchResult Match(const CHART * tstring, int length, int start, CContext * pContext = 0) const; + MatchResult Match(CContext * pContext) const; + CContext * PrepareMatch(const CHART * tstring, index_t start = -1, CContext * pContext = 0) const; + CContext * PrepareMatch(const CHART * tstring, index_t length, index_t start, CContext * pContext = 0) const; + CHART * Replace(const CHART * tstring, const CHART * replaceto, index_t start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const; + CHART * Replace(const CHART * tstring, index_t string_length, const CHART * replaceto, index_t to_length, index_t & result_length, index_t start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const; + int GetNamedGroupNumber(const CHART * group_name) const; + +public: + static void ReleaseString(CHART * tstring); + static void ReleaseContext(CContext * pContext); + +public: + CBuilderT m_builder; +}; + +// +// Implementation +// +template CRegexpT ::CRegexpT(const CHART * pattern, int flags) +{ + Compile(pattern, CBufferRefT(pattern).GetSize(), flags); +} + +template CRegexpT ::CRegexpT(const CHART * pattern, index_t length, int flags) +{ + Compile(pattern, length, flags); +} + +template inline void CRegexpT ::Compile(const CHART * pattern, int flags) +{ + Compile(pattern, CBufferRefT(pattern).GetSize(), flags); +} + +template void CRegexpT ::Compile(const CHART * pattern, index_t length, int flags) +{ + m_builder.Clear(); + if (pattern != 0) m_builder.Build(CBufferRefT(pattern, length), flags); +} + +template inline MatchResult CRegexpT ::MatchExact(const CHART * tstring, CContext * pContext) const +{ + return MatchExact(tstring, CBufferRefT(tstring).GetSize(), pContext); +} + +template MatchResult CRegexpT ::MatchExact(const CHART * tstring, int length, CContext * pContext) const +{ + if (m_builder.m_pTopElx == 0) + return 0; + + // info + int endpos = 0; + + CContext context; + if (pContext == 0) pContext = &context; + + pContext->m_stack.Restore(0); + pContext->m_capturestack.Restore(0); + pContext->m_captureindex.Restore(0); + + pContext->m_nParenZindex = 0; + pContext->m_nLastBeginPos = -1; + pContext->m_pMatchString = (void*)tstring; + pContext->m_pMatchStringLength = length; + pContext->m_nCursiveLimit = 100; + + if (m_builder.m_nFlags & RIGHTTOLEFT) + { + pContext->m_nBeginPos = length; + pContext->m_nCurrentPos = length; + endpos = 0; + } + else + { + pContext->m_nBeginPos = 0; + pContext->m_nCurrentPos = 0; + endpos = length; + } + + pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1); + pContext->m_captureindex[0] = 0; + pContext->m_capturestack.Push(0); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(-1); + + // match + if (!m_builder.m_pTopElx->Match(pContext)) + return 0; + else + { + while (pContext->m_nCurrentPos != endpos) + { + if (!m_builder.m_pTopElx->MatchNext(pContext)) + return 0; + else + { + if (pContext->m_nLastBeginPos == pContext->m_nBeginPos && pContext->m_nBeginPos == pContext->m_nCurrentPos) + return 0; + else + pContext->m_nLastBeginPos = pContext->m_nCurrentPos; + } + } + + // end pos + pContext->m_capturestack[2] = pContext->m_nCurrentPos; + + return MatchResult(pContext, m_builder.m_nMaxNumber); + } +} + +template MatchResult CRegexpT ::Match(const CHART * tstring, int start, CContext * pContext) const +{ + return Match(tstring, CBufferRefT(tstring).GetSize(), start, pContext); +} + +template MatchResult CRegexpT ::Match(const CHART * tstring, int length, int start, CContext * pContext) const +{ + if (m_builder.m_pTopElx == 0) + return 0; + + CContext context; + if (pContext == 0) pContext = &context; + + PrepareMatch(tstring, length, start, pContext); + + return Match(pContext); +} + +template MatchResult CRegexpT ::Match(CContext * pContext) const +{ + if (m_builder.m_pTopElx == 0) + return 0; + + index_t endpos; + int delta; + + if (m_builder.m_nFlags & RIGHTTOLEFT) + { + endpos = -1; + delta = -1; + } + else + { + endpos = pContext->m_pMatchStringLength + 1; + delta = 1; + } + + while (pContext->m_nCurrentPos != endpos) + { + pContext->m_captureindex.Restore(0); + pContext->m_stack.Restore(0); + pContext->m_capturestack.Restore(0); + + pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1); + pContext->m_captureindex[0] = 0; + pContext->m_capturestack.Push(0); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(-1); + + if (m_builder.m_pTopElx->Match(pContext)) + { + pContext->m_capturestack[2] = pContext->m_nCurrentPos; + + // zero width + if (pContext->m_capturestack[1] == pContext->m_nCurrentPos) + { + pContext->m_nCurrentPos += delta; + } + + // save pos + pContext->m_nLastBeginPos = pContext->m_nBeginPos; + pContext->m_nBeginPos = pContext->m_nCurrentPos; + + // return + return MatchResult(pContext, m_builder.m_nMaxNumber); + } + else + { + pContext->m_nCurrentPos += delta; + } + } + + return 0; +} + +template inline CContext * CRegexpT ::PrepareMatch(const CHART * tstring, index_t start, CContext * pContext) const +{ + return PrepareMatch(tstring, CBufferRefT(tstring).GetSize(), start, pContext); +} + +template CContext * CRegexpT ::PrepareMatch(const CHART * tstring, index_t length, index_t start, CContext * pContext) const +{ + if (m_builder.m_pTopElx == 0) + return 0; + + if (pContext == 0) pContext = new CContext(); + + pContext->m_nParenZindex = 0; + pContext->m_nLastBeginPos = -1; + pContext->m_pMatchString = (void*)tstring; + pContext->m_pMatchStringLength = length; + pContext->m_nCursiveLimit = 100; + + if (start < 0) + { + if (m_builder.m_nFlags & RIGHTTOLEFT) + { + pContext->m_nBeginPos = length; + pContext->m_nCurrentPos = length; + } + else + { + pContext->m_nBeginPos = 0; + pContext->m_nCurrentPos = 0; + } + } + else + { + if (start > length) start = length + ((m_builder.m_nFlags & RIGHTTOLEFT) ? 0 : 1); + + pContext->m_nBeginPos = start; + pContext->m_nCurrentPos = start; + } + + return pContext; +} + +template inline int CRegexpT ::GetNamedGroupNumber(const CHART * group_name) const +{ + return m_builder.GetNamedNumber(group_name); +} + +template CHART * CRegexpT ::Replace(const CHART * tstring, const CHART * replaceto, index_t start, int ntimes, MatchResult * result, CContext * pContext) const +{ + index_t result_length = 0; + return Replace(tstring, CBufferRefT(tstring).GetSize(), replaceto, CBufferRefT(replaceto).GetSize(), result_length, start, ntimes, result, pContext); +} + +template CHART * CRegexpT ::Replace(const CHART * tstring, index_t string_length, const CHART * replaceto, index_t to_length, index_t & result_length, index_t start, int ntimes, MatchResult * remote_result, CContext * oContext) const +{ + if (m_builder.m_pTopElx == 0) return 0; + + // --- compile replace to --- + + CBufferT compiledto; + + static const CHART rtoptn[] = { RCHART('\\'), RCHART('$'), RCHART('('), RCHART('?'), RCHART(':'), RCHART('['), RCHART('$'), RCHART('&'), RCHART('`'), RCHART('\''), RCHART('+'), RCHART('_'), RCHART('\\'), RCHART('d'), RCHART(']'), RCHART('|'), RCHART('\\'), RCHART('{'), RCHART('.'), RCHART('*'), RCHART('?'), RCHART('\\'), RCHART('}'), RCHART(')'), RCHART('\0') }; + static CRegexpT rtoreg(rtoptn); + + MatchResult local_result(0), *result = remote_result ? remote_result : &local_result; + + // prepare + CContext * pContext = rtoreg.PrepareMatch(replaceto, to_length, -1, oContext); + index_t lastIndex = 0; + index_t nmatch = 0; + + while (((*result) = rtoreg.Match(pContext)).IsMatched()) + { + index_t delta = result->GetStart() - lastIndex; + if (delta > 0) + { + delta = result->GetStart() - lastIndex; + compiledto.Push(lastIndex); + compiledto.Push(delta); + } + + lastIndex = result->GetStart(); + delta = 2; + + switch (replaceto[lastIndex + 1]) + { + case RCHART('$'): + compiledto.Push(lastIndex); + compiledto.Push(1); + break; + + case RCHART('&'): + case RCHART('`'): + case RCHART('\''): + case RCHART('+'): + case RCHART('_'): + compiledto.Push(-1); + compiledto.Push((int)replaceto[lastIndex + 1]); + break; + + case RCHART('{'): + delta = result->GetEnd() - result->GetStart(); + nmatch = m_builder.GetNamedNumber(CBufferRefT (replaceto + (lastIndex + 2), delta - 3)); + + if (nmatch > 0 && nmatch <= m_builder.m_nMaxNumber) + { + compiledto.Push(-2); + compiledto.Push(nmatch); + } + else + { + compiledto.Push(lastIndex); + compiledto.Push(delta); + } + break; + + default: + nmatch = 0; + for (delta = 1; delta <= 3; delta++) + { + CHART ch = replaceto[lastIndex + delta]; + + if (ch < RCHART('0') || ch > RCHART('9')) + break; + + nmatch = nmatch * 10 + (ch - RCHART('0')); + } + + if (nmatch > m_builder.m_nMaxNumber) + { + while (nmatch > m_builder.m_nMaxNumber) + { + nmatch /= 10; + delta--; + } + + if (nmatch == 0) + { + delta = 1; + } + } + + if (delta == 1) + { + compiledto.Push(lastIndex); + compiledto.Push(1); + } + else + { + compiledto.Push(-2); + compiledto.Push(nmatch); + } + break; + } + + lastIndex += delta; + } + + if (lastIndex < to_length) + { + compiledto.Push(lastIndex); + compiledto.Push(to_length - lastIndex); + } + + int rightleft = m_builder.m_nFlags & RIGHTTOLEFT; + + index_t cmplSize = compiledto.GetSize(); + index_t tb = rightleft ? cmplSize - 2 : 0; + index_t te = rightleft ? -2 : cmplSize; + index_t ts = rightleft ? -2 : 2; + + // --- compile complete --- + + index_t beginpos = rightleft ? string_length : 0; + index_t endpos = rightleft ? 0 : string_length; + + index_t toIndex0 = 0; + index_t toIndex1 = 0; + index_t i; + int ntime; + + CBufferT buffer; + + // prepare + pContext = PrepareMatch(tstring, string_length, start, pContext); + lastIndex = beginpos; + + // Match + for (ntime = 0; ntimes < 0 || ntime < ntimes; ntime++) + { + (*result) = Match(pContext); + + if (!result->IsMatched()) + break; + + // before + if (rightleft) + { + index_t distance = lastIndex - result->GetEnd(); + if (distance) + { + buffer.Push(tstring + result->GetEnd()); + buffer.Push((const CHART *)distance); + + toIndex1 -= distance; + } + lastIndex = result->GetStart(); + } + else + { + index_t distance = result->GetStart() - lastIndex; + if (distance) + { + buffer.Push(tstring + lastIndex); + buffer.Push((const CHART *)distance); + + toIndex1 += distance; + } + lastIndex = result->GetEnd(); + } + + toIndex0 = toIndex1; + + // middle + for (i = tb; i != te; i += ts) + { + index_t off = compiledto[i]; + index_t len = compiledto[i + 1]; + + const CHART * sub = replaceto + off; + + if (off == -1) + { + switch (RCHART(len)) + { + case RCHART('&'): + sub = tstring + result->GetStart(); + len = result->GetEnd() - result->GetStart(); + break; + + case RCHART('`'): + sub = tstring; + len = result->GetStart(); + break; + + case RCHART('\''): + sub = tstring + result->GetEnd(); + len = string_length - result->GetEnd(); + break; + + case RCHART('+'): + for (nmatch = result->MaxGroupNumber(); nmatch >= 0; nmatch--) + { + if (result->GetGroupStart(nmatch) >= 0) break; + } + sub = tstring + result->GetGroupStart(nmatch); + len = result->GetGroupEnd(nmatch) - result->GetGroupStart(nmatch); + break; + + case RCHART('_'): + sub = tstring; + len = string_length; + break; + } + } + else if (off == -2) + { + //TODO:@@@ check to use nmatch instead of len here ??? + int l = static_cast(len); + sub = tstring + result->GetGroupStart(l); + len = result->GetGroupEnd(l) - result->GetGroupStart(l); + } + + buffer.Push(sub); + buffer.Push((const CHART *)len); + + toIndex1 += rightleft ? (-len) : len; + } + } + + // after + if (rightleft) + { + if (endpos < lastIndex) + { + buffer.Push(tstring + endpos); + buffer.Push((const CHART *)(lastIndex - endpos)); + } + } + else + { + if (lastIndex < endpos) + { + buffer.Push(tstring + lastIndex); + buffer.Push((const CHART *)(endpos - lastIndex)); + } + } + + if (oContext == 0) ReleaseContext(pContext); + + // join string + result_length = 0; + for (i = 0; i < buffer.GetSize(); i += 2) + { + result_length += (index_t)buffer[i + 1]; + } + + CBufferT result_string; + result_string.Prepare(result_length); + result_string.Restore(0); + + if (rightleft) + { + for (i = buffer.GetSize() - 2; i >= 0; i -= 2) + { + result_string.Append(buffer[i], (index_t)buffer[i + 1]); + } + } + else + { + for (i = 0; i < buffer.GetSize(); i += 2) + { + result_string.Append(buffer[i], (index_t)buffer[i + 1]); + } + } + + result_string.Append(0); + + result->m_result.Append(result_length, 3); + result->m_result.Append(ntime); + + if (rightleft) + { + result->m_result.Append(result_length - toIndex1); + result->m_result.Append(result_length - toIndex0); + } + else + { + result->m_result.Append(toIndex0); + result->m_result.Append(toIndex1); + } + + return result_string.Detach(); +} + +template inline void CRegexpT ::ReleaseString(CHART * tstring) +{ + if (tstring != 0) free(tstring); +} + +template inline void CRegexpT ::ReleaseContext(CContext * pContext) +{ + if (pContext != 0) delete pContext; +} + +// +// All implementations +// +template CAlternativeElxT ::CAlternativeElxT() +{} + +template int CAlternativeElxT ::Match(CContext * pContext) const +{ + if (m_elxlist.GetSize() == 0) + return 1; + + // try all + for (int n = 0; n < m_elxlist.GetSize(); n++) + { + if (m_elxlist[n]->Match(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + } + + return 0; +} + +template int CAlternativeElxT ::MatchNext(CContext * pContext) const +{ + if (m_elxlist.GetSize() == 0) + return 0; + + index_t n = 0; + + // recall prev + pContext->m_stack.Pop(n); + + // prev + if (m_elxlist[n]->MatchNext(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + else + { + // try rest + for (n++; n < m_elxlist.GetSize(); n++) + { + if (m_elxlist[n]->Match(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + } + + return 0; + } +} + +// assertx.cpp: implementation of the CAssertElx class. +// +template CAssertElxT ::CAssertElxT(ElxInterface * pelx, int byes) +{ + m_pelx = pelx; + m_byes = byes; +} + +template int CAssertElxT ::Match(CContext * pContext) const +{ + index_t nbegin = pContext->m_nCurrentPos; + index_t nsize = pContext->m_stack.GetSize(); + index_t ncsize = pContext->m_capturestack.GetSize(); + int bsucc; + + // match + if (m_byes) + bsucc = m_pelx->Match(pContext); + else + bsucc = !m_pelx->Match(pContext); + + // status + pContext->m_stack.Restore(nsize); + pContext->m_nCurrentPos = nbegin; + + if (bsucc) + pContext->m_stack.Push(ncsize); + else + pContext->m_capturestack.Restore(ncsize); + + return bsucc; +} + +template int CAssertElxT ::MatchNext(CContext * pContext) const +{ + index_t ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_capturestack.Restore(ncsize); + + return 0; +} + +// emptyelx.cpp: implementation of the CEmptyElx class. +// +template CEmptyElxT ::CEmptyElxT() +{} + +template int CEmptyElxT ::Match(CContext *) const +{ + return 1; +} + +template int CEmptyElxT ::MatchNext(CContext *) const +{ + return 0; +} + +// globalx.cpp: implementation of the CGlobalElx class. +// +template CGlobalElxT ::CGlobalElxT() +{} + +template int CGlobalElxT ::Match(CContext * pContext) const +{ + return pContext->m_nCurrentPos == pContext->m_nBeginPos; +} + +template int CGlobalElxT ::MatchNext(CContext *) const +{ + return 0; +} + +// greedelx.cpp: implementation of the CGreedyElx class. +// +template CGreedyElxT ::CGreedyElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT (pelx, nmin) +{ + m_nvart = nmax - nmin; +} + +template int CGreedyElxT ::Match(CContext * pContext) const +{ + if (!CRepeatElxT ::MatchFixed(pContext)) + return 0; + + while (!MatchVart(pContext)) + { + if (!CRepeatElxT ::MatchNextFixed(pContext)) + return 0; + } + + return 1; +} + +template int CGreedyElxT ::MatchNext(CContext * pContext) const +{ + if (MatchNextVart(pContext)) + return 1; + + if (!CRepeatElxT ::MatchNextFixed(pContext)) + return 0; + + while (!MatchVart(pContext)) + { + if (!CRepeatElxT ::MatchNextFixed(pContext)) + return 0; + } + + return 1; +} + +template int CGreedyElxT ::MatchVart(CContext * pContext) const +{ + int n = 0; + index_t nbegin00 = pContext->m_nCurrentPos; + index_t nsize = pContext->m_stack.GetSize(); + index_t ncsize = pContext->m_capturestack.GetSize(); + + while (n < m_nvart && CRepeatElx::MatchForward(pContext)) + { + n++; + } + + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(nsize); + pContext->m_stack.Push(pContext->m_nCurrentPos); + pContext->m_stack.Push(1); + pContext->m_stack.Push(nbegin00); + pContext->m_stack.Push(n); + + return 1; +} + +template int CGreedyElxT ::MatchNextVart(CContext * pContext) const +{ + index_t n, nbegin00, nsize, ncsize; + CSortedBufferT nbegin99; + pContext->m_stack.Pop(n); + pContext->m_stack.Pop(nbegin00); + pContext->m_stack.Pop(nbegin99); + pContext->m_stack.Pop(nsize); + pContext->m_stack.Pop(ncsize); + + if (n == 0) return 0; + + index_t n0 = n; + + if (!CRepeatElxT::m_pelx->MatchNext(pContext)) + { + n--; + } + + // not to re-match + else if (pContext->m_nCurrentPos == nbegin00) + { + pContext->m_stack.Restore(nsize); + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin00; + + return 0; + } + + // fix 2012-10-26, thanks to chenlx01@sohu.com + else + { + CContextShot shot(pContext); + + while (n < m_nvart && CRepeatElx::MatchForward(pContext)) + { + n++; + } + + if (nbegin99.Find(pContext->m_nCurrentPos) >= 0) + { + shot.Restore(pContext); + n = n0; + } + else + { + nbegin99.Add(pContext->m_nCurrentPos); + } + } + + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(nsize); + pContext->m_stack.Push(nbegin99); + pContext->m_stack.Push(nbegin00); + pContext->m_stack.Push(n); + + return 1; +} + +// indepelx.cpp: implementation of the CIndependentElx class. +// +template CIndependentElxT ::CIndependentElxT(ElxInterface * pelx) +{ + m_pelx = pelx; +} + +template int CIndependentElxT ::Match(CContext * pContext) const +{ + index_t nbegin = pContext->m_nCurrentPos; + index_t nsize = pContext->m_stack.GetSize(); + index_t ncsize = pContext->m_capturestack.GetSize(); + + // match + int bsucc = m_pelx->Match(pContext); + + // status + pContext->m_stack.Restore(nsize); + + if (bsucc) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(ncsize); + } + + return bsucc; +} + +template int CIndependentElxT ::MatchNext(CContext * pContext) const +{ + index_t nbegin = 0, ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_stack.Pop(nbegin); + + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin; + + return 0; +} + +// listelx.cpp: implementation of the CListElx class. +// +template CListElxT ::CListElxT(int brightleft) +{ + m_brightleft = brightleft; +} + +template int CListElxT ::Match(CContext * pContext) const +{ + if (m_elxlist.GetSize() == 0) + return 1; + + // prepare + index_t bol = m_brightleft ? m_elxlist.GetSize() : -1; + index_t stp = m_brightleft ? -1 : 1; + index_t eol = m_brightleft ? -1 : m_elxlist.GetSize(); + + // from first + index_t n = bol + stp; + + // match all + while (n != eol) + { + if (m_elxlist[n]->Match(pContext)) + { + n += stp; + } + else + { + n -= stp; + + while (n != bol && !m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if (n != bol) + n += stp; + else + return 0; + } + } + + return 1; +} + +template int CListElxT ::MatchNext(CContext * pContext) const +{ + if (m_elxlist.GetSize() == 0) + return 0; + + // prepare + index_t bol = m_brightleft ? m_elxlist.GetSize() : -1; + index_t stp = m_brightleft ? -1 : 1; + index_t eol = m_brightleft ? -1 : m_elxlist.GetSize(); + + // from last + index_t n = eol - stp; + + while (n != bol && !m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if (n != bol) + n += stp; + else + return 0; + + // match rest + while (n != eol) + { + if (m_elxlist[n]->Match(pContext)) + { + n += stp; + } + else + { + n -= stp; + + while (n != bol && !m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if (n != bol) + n += stp; + else + return 0; + } + } + + return 1; +} + +// mresult.cpp: implementation of the MatchResult class. +// +template MatchResultT ::MatchResultT(CContext * pContext, index_t nMaxNumber) +{ + if (pContext != 0) + { + m_result.Prepare(nMaxNumber * 2 + 3, -1); + + // matched + m_result[0] = 1; + m_result[1] = nMaxNumber; + + for (int n = 0; n <= nMaxNumber; n++) + { + index_t index = pContext->m_captureindex[n]; + //if( index < 0 ) continue; + if (!CBracketElxT::CheckCaptureIndex(index, pContext, n)) continue; + // check enclosed + index_t pos1 = pContext->m_capturestack[index + 1]; + index_t pos2 = pContext->m_capturestack[index + 2]; + + // info + m_result[n * 2 + 2] = pos1 < pos2 ? pos1 : pos2; + m_result[n * 2 + 3] = pos1 < pos2 ? pos2 : pos1; + } + } +} + +template inline int MatchResultT ::IsMatched() const +{ + return static_cast(m_result.At(0, 0)); +} + +template inline index_t MatchResultT ::MaxGroupNumber() const +{ + return m_result.At(1, 0); +} + +template inline index_t MatchResultT ::GetStart() const +{ + return m_result.At(2, -1); +} + +template inline index_t MatchResultT ::GetEnd() const +{ + return m_result.At(3, -1); +} + +template inline index_t MatchResultT ::GetGroupStart(index_t nGroupNumber) const +{ + return m_result.At(2 + nGroupNumber * 2, -1); +} + +template inline index_t MatchResultT ::GetGroupEnd(index_t nGroupNumber) const +{ + return m_result.At(2 + nGroupNumber * 2 + 1, -1); +} + +template MatchResultT & MatchResultT :: operator = (const MatchResultT & result) +{ + m_result.Restore(0); + if (result.m_result.GetSize() > 0) m_result.Append(result.m_result.GetBuffer(), result.m_result.GetSize()); + + return *this; +} + +// posselx.cpp: implementation of the CPossessiveElx class. +// +template CPossessiveElxT ::CPossessiveElxT(ElxInterface * pelx, int nmin, int nmax) : CGreedyElxT (pelx, nmin, nmax) +{} + +template int CPossessiveElxT ::Match(CContext * pContext) const +{ + index_t nbegin = pContext->m_nCurrentPos; + index_t nsize = pContext->m_stack.GetSize(); + index_t ncsize = pContext->m_capturestack.GetSize(); + int bsucc = 1; + + // match + if (!CRepeatElxT ::MatchFixed(pContext)) + { + bsucc = 0; + } + else + { + while (!CGreedyElxT ::MatchVart(pContext)) + { + if (!CRepeatElxT ::MatchNextFixed(pContext)) + { + bsucc = 0; + break; + } + } + } + + // status + pContext->m_stack.Restore(nsize); + + if (bsucc) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(ncsize); + } + + return bsucc; +} + +template int CPossessiveElxT ::MatchNext(CContext * pContext) const +{ + index_t nbegin = 0, ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_stack.Pop(nbegin); + + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin; + + return 0; +} + +// reluctx.cpp: implementation of the CReluctantElx class. +// +template CReluctantElxT ::CReluctantElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT (pelx, nmin) +{ + m_nvart = nmax - nmin; +} + +template int CReluctantElxT ::Match(CContext * pContext) const +{ + if (!CRepeatElxT ::MatchFixed(pContext)) + return 0; + + while (!MatchVart(pContext)) + { + if (!CRepeatElxT ::MatchNextFixed(pContext)) + return 0; + } + + return 1; +} + +template int CReluctantElxT ::MatchNext(CContext * pContext) const +{ + if (MatchNextVart(pContext)) + return 1; + + if (!CRepeatElxT ::MatchNextFixed(pContext)) + return 0; + + while (!MatchVart(pContext)) + { + if (!CRepeatElxT ::MatchNextFixed(pContext)) + return 0; + } + + return 1; +} + +template int CReluctantElxT ::MatchVart(CContext * pContext) +{ + pContext->m_stack.Push(0); + + return 1; +} + +template int CReluctantElxT ::MatchNextVart(CContext * pContext) const +{ + index_t n = 0, nbegin = pContext->m_nCurrentPos; + + pContext->m_stack.Pop(n); + + if (n < m_nvart && CRepeatElxT ::m_pelx->Match(pContext)) + { + while (pContext->m_nCurrentPos == nbegin) + { + if (!CRepeatElxT ::m_pelx->MatchNext(pContext)) break; + } + + if (pContext->m_nCurrentPos != nbegin) + { + n++; + + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(n); + + return 1; + } + } + + while (n > 0) + { + pContext->m_stack.Pop(nbegin); + + while (CRepeatElxT ::m_pelx->MatchNext(pContext)) + { + if (pContext->m_nCurrentPos != nbegin) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(n); + + return 1; + } + } + + n--; + } + + return 0; +} + +// repeatx.cpp: implementation of the CRepeatElx class. +// +template CRepeatElxT ::CRepeatElxT(ElxInterface * pelx, int ntimes) +{ + m_pelx = pelx; + m_nfixed = ntimes; +} + +template int CRepeatElxT ::Match(CContext * pContext) const +{ + return MatchFixed(pContext); +} + +template int CRepeatElxT ::MatchNext(CContext * pContext) const +{ + return MatchNextFixed(pContext); +} + +template int CRepeatElxT ::MatchFixed(CContext * pContext) const +{ + if (m_nfixed == 0) + return 1; + + int n = 0; + + while (n < m_nfixed) + { + if (m_pelx->Match(pContext)) + { + n++; + } + else + { + n--; + + while (n >= 0 && !m_pelx->MatchNext(pContext)) + n--; + + if (n >= 0) + n++; + else + return 0; + } + } + + return 1; +} + +template int CRepeatElxT ::MatchNextFixed(CContext * pContext) const +{ + if (m_nfixed == 0) + return 0; + + // from last + int n = m_nfixed - 1; + + while (n >= 0 && !m_pelx->MatchNext(pContext)) + n--; + + if (n >= 0) + n++; + else + return 0; + + // match rest + while (n < m_nfixed) + { + if (m_pelx->Match(pContext)) + { + n++; + } + else + { + n--; + + while (n >= 0 && !m_pelx->MatchNext(pContext)) + n--; + + if (n >= 0) + n++; + else + return 0; + } + } + + return 1; +} + +// Regexp +typedef CRegexpT CRegexpA; +typedef CRegexpT CRegexpW; + +#if defined(_UNICODE) || defined(UNICODE) +typedef CRegexpW CRegexp; +#else +typedef CRegexpA CRegexp; +#endif + +} + +#endif//__DEELX_REGEXP64__H__ diff --git a/scintilla/deelx/doc/Deelx - Regular Expression Engine - Features.url b/scintilla/deelx/doc/Deelx - Regular Expression Engine - Features.url new file mode 100644 index 000000000..d4f73160e --- /dev/null +++ b/scintilla/deelx/doc/Deelx - Regular Expression Engine - Features.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=http://www.regexlab.com/en/deelx/introidx.htm diff --git a/scintilla/deelx/doc/Deelx Regular Expression Syntax Reference.txt b/scintilla/deelx/doc/Deelx Regular Expression Syntax Reference.txt new file mode 100644 index 000000000..003c5821f --- /dev/null +++ b/scintilla/deelx/doc/Deelx Regular Expression Syntax Reference.txt @@ -0,0 +1,426 @@ +Regular Expression Syntax Reference http://www.regexlab.com/en/regref.htm + + [All rights reserved: http://www.regexlab.com/en/regref.htm] + [Author: sswater shi (sswater@gmail.com)] + + + Introduction + + Regular expression is to express a characteristic in a string, and then to match another string + with the characteristic. For example, pattern "ab+" means "one 'a' and at least one 'b' ", so "ab", + "abb", "abbbbbbb" match the pattern. + + Regular expression is used to : (1) test a string whether it matches a pattern, such as a email + address. (2) to find a substring which matches certain pattern, from a whole text. (3) to do + complex replacement in a text. + + It is very simple to study regular expression syntax, and the few abstract concepts can be + understood easily too. Many articles does not introduce its concepts from simple ones to + abstract ones step by step, so some persons may feel it is difficult to study. On the other hand, + each regular expression engine's document will describe its special function, but this part of + special function is not what we should study first. + + + 1. Regular Expression Basic Syntax + + 1.1 Common Characters + + Letters, numbers, the underline, and punctuations with no special definition are "common + characters". When regular expression matches a string, a common character can match the + same character. + + - Example1: When pattern "c" matches string "abcde", match result: success; substring + matched: "c"; position: starts at 2, ends at 3. + + - Example2: When pattern "bcd" matches string "abcde",match result: success; substring + matched: "bcd"; position: starts at 1, ends at 4. + + + 1.2 Simple escaped characters + + Nonprinting characters which we know: + + Expression Matches + \r, \n Carriage return, newline character + \t Tabs + \\ Matches "\" itself + + Some punctuations are specially defined in regular expression. To match these characters in + string, add "\" in pattern. For example: ^, $ has special definition, so we need to use "\^" and + "\$" to match them. + + + Expression Matches + \^ Matches "^" itself + \$ Matches "$" itself + + \. Matches dot(.) itself + + These escaped characters have the same effect as "common characters": to match a certain + character. + + + - Example1: When pattern "\$d" matches string "abc$de", match result: success; substring + matched: "$d"; position: starts at 3, ends at 5. + + + 1.3 Expression matches anyone of many characters + + Some expressions can match anyone of many characters. For example: "\d" can match any + number character. Each of these expressions can match only one character at one time, though + they can match any character of a certain group of characters. + + Expression Matches + + \d Any digit character, any one of 0~9 + \w Any alpha, numeric, underline, any one of A~Z,a~z,0~9,_ + \s Any one of space, tab, newline, return, or newpage character + + . '.' matches any character except the newline character(\n) + + - Example1: When pattern "\d\d" matches "abc123", match result: success; substring + matched: "12"; position: starts at 3, ends at 5. + + - Example2: When pattern "a.\d" matches "aaa100", match result: success; substring + matched: "aa1"; position: starts at 1, ends at 4. + + + 1.4 Custom expression matches anyone of many characters + + Expression uses square brackets [ ] to contain a series of characters, it can match anyone of + them. Uses [^ ] to contain a series of characters, it can match anyone character except + characters contained. + + Expression Matches + + [ab5@] Matches "a" or "b" or "5" or "@" + [^abc] Matches any character except "a","b","c" + + [f-k] Any character among "f"~"k" + [^A-F0-3] Any character except "A"~"F","0"~"3" + + - Example1: When pattern "[bcd][bcd]" matches "abc123" , match result: success; substring + matched: "bc"; position: starts at 1, ends at 3. + + - Example2: When pattern "[^abc]" matches "abc123", match result: success; substring + matched: "1"; position: starts at 3, ends at 4. + + + 1.5 Special expression to quantify matching + + All expressions introduced before can match character only one time. If a expression is + followed by a quantifier, it can matches more than one times. + + + For example: we can use the pattern "[bcd]{2}" instead of "[bcd][bcd]". + + Expression Function + Match exactly n times, example: "\w{2}" equals "\w\w"; "a{5}" + {n} + equals "aaaaa" + {m,n} At least m but no more than n times: "ba{1,3}" matches "ba","baa","baaa" + + {m,} Match at least n times: "\w\d{2,}" matches "a12","_456","M12344"... + ? Match 1 or 0 times, equivalent to {0,1}: "a[cd]?" matches "a","ac","ad". + + + Match 1 or more times, equivalent to {1,}: "a+b" matches "ab","aab","aaab"... + * Match 0 or more times, equivalent to {0,}: "\^*b" matches "b","^^^b"... + + + - Example1: When pattern "\d+\.?\d*" matches "It costs $12.5", match result: success; + substring matched:"12.5"; position: starts at 10, ends at 14. + + + - Example2: When pattern "go{2,8}gle" matches "Ads by goooooogle", match result: + success; substring matched: "goooooogle"; position: starts at 7, ends at 17. + + + + 1.6 Some special punctuations with abstract function + + Some punctuations in pattern have special function: + + Expression Function + ^ Match the beginning of the string + $ Match the end of the string + + \b Match a word boundary + + More examples to help you to understand. + + - Example1: When pattern "^aaa" matches "xxx aaa xxx", match result: failed. Because "^" + must match the beginning of the string. It could match successfully on condition that "aaa" is + at the beginning of the string, such as "aaa xxx xxx". + + - Example2: When pattern "aaa$" matches "xxx aaa xxx", match result: failed. Bacause "$" + must match the end of the string. It could match successfully on condition that "aaa" is at the + end of the string, such as "xxx xxx aaa". + + - Example3: When pattern ".\b." matches "@@@abc", match result: success; substring + matched: "@a"; position: starts at 2, ends at 4. + Further explanation: "\b" is similar to "^" and "$", matches no character itself, but it require a + '\w' character at its one side, another not '\w' character at the other side. + + + - Example4: When pattern "\bend\b" matches "weekend,endfor,end", match result: + success; substring matched: "end"; position: starts at 15, ends at 18. + + Some special punctuation can make effect on other sub-patterns: + + + Expression Function + | Alternation, matches either left side or right side + (1). Let sub-patterns in it to be a whole part when it is quantified. + ( ) + (2). Match result of sub-patterns in it can be retrieved individually + + - Example5: When pattern "Tom|Jack" matches string "I'm Tom, he is Jack", match result: + success; substring matched: "Tom"; position: starts at 4, ends at 7. When match next, match + result: success; substring matched: "Jack"; position: starts at 15, ends at 19. + + + - Example6: When pattern "(go\s*)+" matches "Let's go go go!", match result: success; + substring matched: "go go go"; position: starts at 6, ends at 14. + + - Example7: When pattern "?(\d+\.?\d*)" matches "$10.9,?20.5", match result: success; + substring matched: "?20.5"; position: starts at 6, ends at 10. Match result of sub-patterns + in "( )" is: "20.5". + + + 2. Regular expression advanced syntax + + 2.1 Reluctant or greedy quantifiers + + There are serval method to quantify subpattern, such as: "{m,n}", "{m,}", "?", "*", "+". By + default, a quantified subpattern is "greedy", that is, it will match as many times as possible + (given a particular starting location) while still allowing the rest of the pattern to match. For + example, to match "dxxxdxxxd": + + Expression Match result + + (d)(\w+) "\w+" matches all characters "xxxdxxxd" behind of "d" + "\w+" matches all characters "xxxdxxx" between the first "d" and the last + (d)(\w+)(d) "d". In order to let the whole pattern match success, "\w" has to give up the + last "d", although it can match the last "d" too. + + Thus it can be seen that: when "\w+" matches, it will match as many characters as possible. + In the second example, it does not match the last "d", but this is in order to let the whole + pattern match successfully. Pattern with "*" or "{m,n}" will also match as many times as + possible, pattern with "?" will match if possible. This type of matching is called "greedy + matching". ? + + + Reluctant Matching: + + To follow the quantifier with a "?", it can let the pattern to match the minimum number of + times possible. This type of matching is called reluctant matching. In order to let the whole + pattern match successfully, the reluctant pattern may match a few more times if it is required. + For example, to match "dxxxdxxxd": + + Expression Match result + + (d)(\w+?) "\w+?" match as few times as possible, so "\w+?" matches only one "x" + In order to let the whole pattern match successfully, "\w+?" has to match + (d)(\w+?)(d) + "xxx". So, match result is: "\w+?" matches "xxx" + + More examples: + + - Example1: When pattern "(.*)" matches "

aa

+

bb

", match result: success; substring matched: the whole + "

aa

bb

", "" in the pattern matches the last + "" in the string. + + - Example2: For comparison, when pattern "(.*?)" matches the string in + example1, it matches "

aa

". When match next, the next "

bb

+ " can be matched. + + + 2.2 Referring to matched substring \1, \2... + + During the process of matching, the match results of subpattern between parentheses "( )" + are recorded for later use. When retrieving match results, those match result of subpattern can + be retrieved individually, and this has been demonstrated many times in former examples. In + practice, parentheses "( )" must be used to get what we want indeed after match, such as + "(.*?)". + + + In fact, those match result of subpattern between parentheses can be used not only after + matching, but also during matching. The latter part of subpattern, can refer the match result of + former subpattern. Usage: "\" plus a number to refer to the corresponding substring. "\1" refers + to 1st pair of parentheses' match result, "\2" refers to 2nd pair of parentheses' match result. + + Examples: + + - Example1: When pattern "('|")(.*?)(\1)" matches " 'Hello', "World" ", match result: success; + substring matched: " 'Hello' "; when match next, substring matched: " "World" ". + + - Example2: When pattern "(\w)\1{4,}" matches "aa bbbb abcdefg ccccc 111121111 + 999999999", match result: success; substring matched: "ccccc"; when match next, substring + matched "999999999". This pattern require a character of "\w" to repeat at least 5 times. + Pay attention to comparison with "\w{5,}". + + - Example3: When pattern "<(\w+)\s*(\w+(=('|").*?\4)?\s*)*>.*?" matches "", match result: success. If both "" and "" are + not "td", the match will fail. + + + 2.2b DEELX Regular Expression Replace Syntax + + $1 ~ $999 - Stands for what a certain group captured. If the number is larger than the max group number, + DEELX will use less digitals, till the number is smaller than or equal to the max group number. + For example: + If the max group number is 20, "$999" means "$9" and common string "99", while "$15" means the 15th group. + If you need "$1" and common string "5", you can use $0015 , DEELX at most recognize 3 digitals as number. + + ${name} - Stands for what a named group captured. + $$ - Stands for a single dollars ($). + $& - Stands for what the overall expression captured. + $` - The substring before the beginning of what the overall expression captured in the original text. + $' - The substring behind the end of what the overall expression captured in the original text. + $+ - Stands for what a group captured, which group has the max group number among those groups + that have captured. For example: when "aaa(b+)|ccc(b+)" matches "aaabbb" , + $+ stands for $1, even though $2 has the max group number. + $_ - Stands for the whole original text. + + + 2.3 Lookahead assertion; Lookbehind assertion + In former chapters, I have introduced serval punctuations with special function: + "^","$","\b". They all do not match any characters, but they all require certain conditions on + their position. Now, this chapter will introduce more methods to add conditions on the gap + between characters. + + Lookahead assertion: "(?=xxxxx)", "(?!xxxxx)" + + Format: "(?=xxxxx)", the condition which it add on the gap is that: string on the right side of + the gap must be abe to match the subpattern "xxxxx" between the parentheses. It is just a + condition, not a match operation, so there is no match result. + + - Example1: When pattern "Windows (?=NT|XP)" matches "Windows 98, Windows NT, + Windows 2000", it can match only "Windows " of "Windows NT", the other "Windows " could + not be matched. + + - Example2: When pattern "(\w)((?=\1\1\1)(\1))+" matches "aaa ffffff 999999999", it can + match first 4 "f"s among the 6 "f"s, it can match first 7 "9"s among 9 "9"s. + + Format: "(?!xxxxx)", string on the right side of the gap must not be able to match the + subpattern "xxxxx". + + - Example3: When pattern "((?!\bstop\b).)+" matches "fdjka ljfdl stop fjdsla fdj", it will + match from the beginning of string to the position of "stop". If there is no "stop" in the string, + the pattern will match the whole string. + + + - Example4: When pattern "do(?!\w)" matches "done, do, dog", it can only match "do". + Here, "(?!\w)" has the same effect as "\b". + + Lookbehind assertion: "(?<=xxxxx)", "(?u9w4~8ySqaO?mndH^xOUQH*02o z%&hs*`>sQsUC*w)tDarw98O)3R8bZK0{{R9F9*oW5gy?<3JwhbAU}e57kQ1LWWoco z`iXabqWmxYU*mr@%vS+$|5K_Uzn%>JDW85#`qFr5{ycf@0wh(G6@F<{UJlq_x|i{w zFXET(`KA3*{c69?f9YVr0RYQ*P+fYWF_cfYuRi^<{e}N-3bNud=@7rH{znhO@X~>1 zAdwW&kPuM<$*fY&`lG8N^y0w_1j9h0tfC|-C$0>V`GYnD{$;4Lrm~8tEFggXi_}X` zMP5Nt_ywF){-E&ERaKIC84r@VN)UnY+Eo#f0m)n-3G#lKN?lS;L|$DPB>h^;Uj~kW zR7}#;*+SOP!WJZ>OUwVr1BQWAUPww(PFV#cqx{`R9CQX!b#*}}Cp%*cLuU(Dpp1pB zmD4Ye@&31ff}@>1(9zlB#h3>i<<}5tpog2Cqsd=(82?fVsW<|GAnCW{6*7OU_74jY zLuW&fj6%oYw`l(|lr^+}SqBCc>tD#<+`u73U@)|W{4ZWF#DM(PCNgxIGzpPDwKx=n;V<#t&pdq+um4Dlt*g3!2FX#sf zh5bnD5Ahe1e~b2sDctO@(Z7QX4#Q+Z`#ter>GLbJ z;4n#l4AkGy-y#YQ*Dv>Z^BeekAi)hf;Ua&T{LTO1ZbBM-2FYLBPbcgMG;{`<&Ry9=6Vg zud2{>g)UR*KQ+G+2JQE`k)e~mp|!K)9|;zg$DVKU56H;M(exD>!TwJ4Psq;p^%@Ef z;;BM^^Y;mjZEXKA3a?ctr2Pj1bTWQL*1W5A{(+b|+W&z>eAFoL{KrDJhBmMHAJL*z z4gL?v!Nt(|)#^-b-{>EZ)9(WY|{82RxdmF8g6WhX`#SBAR9Xqn?GWT z-6Q`d@ejz>*znaVF3q-(?;nsO(Aen@tGF(jeV)I<{pFz}@O8fUQqv;#zZ(Hx(&%*y zOiXHxeI6Jz$B6HFuynjc` zfX=V!_AwZ$SPSKEv)R0^^0C6O2J-I~HqL*p|8d)_RQd0S{Y#L4tdf+#{SD^t$gk*M zk#6B?sed5WeV1G>Mil01sI;`%=pPWonMFZa7&52lnd z@V`g-3;8q8QWgYC)c#@P{HIN72u6X|KaoFeQY%mjLH>?dIQ_{@>O5)z;O~fyq5G=` zY5u4MI{)LdlZ*8$n`wo%ol0oj+`k^i%D-;E4010QsEmb^^UJSIlz-PNJKH(_+I(4M zVLO`_W}TdXCJe&Aw%^tnkmDW{>rVTBYq+DG^{>SMmbg^^m*ZbNvX!(owF4MIGyGo; zR&jiJOqkj^+A#d#J}k9tN*L`@lr$q{CZmawsD94bA;cE@^5Vc z>9sR}_9|xtUuCBJt0dQZmEp#(lF;E*f_uD5nc!E69Q`WuQeLH4=BtFvf0aMWU**-q ztNc~aWBug>+aKD0*N%SGhCE+FB?I{Vu~oloMZe@PlivXdtp2WK{W;Np;y(udW8gmq z{$t=j2L5Bs+IP8P^1#yrazFSwY7>xi3(68lBRhE}NvpvP&-0iTGV7JU>R_0{~$|bLb!dnl_3s z*Zx$y+A^%LWAitgBR6x52=15G5%i^l_T^raSOfOQOEAW}TPw$J)9tTY_@O7an(&(g zWp#Jm&lB561~#LSde_C|@>}J(5+=tOdg-)<5v;Jj8is1d6Oz982ByFQu5bldT(PlB zD=S&n{+489D`}Hd<}>&WC@np`KwjJsh9^=fk%j>NCLl}F5r?awhM2xGz~2}^Al!?| z)D`L`Kp|du`{HR5dPt^`Zqv87l7fC2%iIUDfQB)T)MX3F5bhOIpe18d%0G1NBEE_VWA zj+qU;Z~LXEJVZ#Nnt5`3P5i+Fk^I7}s@qLb*ve?K6!Bak#ARV&2n@ky10g#EO{r_O z^Q;dKZZvz3DXTYebZX9qW+bLh;Rp}9y~_(9c`?o(|E#7vbnUtqwcTL&7IXMv28nwv z@i^bFvNUR6Oxnj`fH=Am^BE@vEGvMDwrdguG5B*&HKq4{0%L81Q-VPP^B+%0405E$_i ztIYw{{2e2LUVB_pTwqgOFHWm&BvtY>#4-Sij;ES{$32QhO8gOPvkq;#FMr9=ZH*_c zzQ2Rwyc63~9*aec%{JUdpL+oU5eLzIO;47_rM~@hE*!)>4R<0Au?@kP>W%L)vKV9t zi;gE4AAS0&K;>W`{=tQO4F$3Hi6OCCfo2H@=AwWD|84d+M9?a{gn0waFZscDif*75 z>)Gc>Ox>D%YmWLQn~tj70g$G5Kx=LelhqH5XZzKGPcAy+e!L7=^I}&XXe)8L!{Z1f z^JP0fQV~VQjSl?WBjb#gqvHHh4{?Y@-Kv%+A`qC2Z-G}DDInKI`Mh5F^jDw~t?z?X z1q{63ez?@3oz*bk`{dvA{J>1I@&-y{NKO@#@mUY4NT+s$_ejX)+|(X-vs>Yl+LJD6 z!~EgV>IPy4%1z3}_)~7cr*X=e;4Ag9h{nwbphTy42I_-!J74*8STdzgmmB&$c*}=s6Ty+APo<79{+?L!DxZn4$8xhfnrEOUDmrz%2lC)6iH0{jX67u zwt~ZPJ+47Y6kR+LH0)wRP~triTq-5W2RJw}41qo(r>=HnO?AlxwSuE!B@9jJuG=}5 zvKeHFG7Y){MIO=Ei#a!mueyZ9*l9ZM84mO?adujmIp#$q?W zAJ$IT&c?B$#1Mro42^YP*)F05MImZA8OT>^`f2Qq9LR3h425T}qCsnrzh zf$c0TtW3(O1Y4CKFnGrwYraW5G9eCrykB*ubOHac3a5rvv2jhTk?|l*nqRKnX}<84 z%Q+d!FrKAsEpv5$gD8&VMI;viMqgR7n-A$n!v$6Gsixsd*8=2=-;Yg@Z~VfChnz-g=11{yRBYNe(#O&U_a-|5rK88-S;Ll^oL5>&<1Hn_?FVU!1y`k`JIj(vB{hoSv6VR0fuuG6u3qm*r=X zuZo6`lA|ctxVkmH*ebfIxjAy5?H{{ZVU)A0XcoQot~(O$^m`bnW(gBgY<6>lS8YU&~0F!DnQrW-iH%lMgo91PyT(#g z7eOz8#l#+tE~%G>TiJp4)d_Pz&j(W0%ZE;7hX?FBU99^!0uvhY3y``qa`b6F-g`YQ z9|h&)2}B}gXpU^X_u>g3V`4G?IlJT(Pt%_rZgP+^t+luRDgFCH*hX4AwAVS-J0R{1 z!;-ASblqU_@SwSD|K}~m@IA{fuH}%(o#Ixxx;<0MJ8PlWwx_D#4TW4e`Ck*8Jh^bR89dNVW9*iwbiCJGSZuqD< zR`wk$u`Lzj&W>x($Ql;*CY%^YyH?LHb^tUHkiV%|RGv}{d^Z=VIe{+=(oz=2642s@Q8j5&C%DRuw>g)Bn| zlRUQC)QyyW;l7M8?~ad^yuvn7cBWYM8XWQRLcUa9l}P#=59ox+ArY zuxo4zIn)ilE??uF`wWU=7I9>k*)+1%gsptjNgZP_S05+ynH($l^0gqR z(B|(26e5E2N+II&0`CakIyNhO3mG?)_yM}f_*DcPqSKzZOIis87U!O7?dx+e$uyUN zL2N^QSMeUcsrlDDeN0jBjOcCsrU)%VD0C*&$|_QvvCxc)!?*Elk>L3o!a+t)w{Ucb z7K$swkujmL27MnDgT7-cxs!|+RhzD0IlSF$zw>V9fs}XU^9TLFh1O{>RK;rF6qlqg zK|NAlfOPT!4CirS>(-DKR#=3R;yQDpFmRnTnQ+JEp(&s^f4Rbr&5Vyr^jvJ)Mpo5P z-6;8X+2XLDA2*qRDpjX0H1fyWi-On>Ug~KG6s`@FT8Iq|LT;mdbTB%)3X?Dr1x@sk zyEIhw2P?!vdKhQCx1vG?`G^r%$jxykinUstR!B+Mv&r;mD;F2_nt3X4{_B{Dxh$;v z6&vqb-X)@~_U_}}`1FEAGkAVnHmSuy15r7O2l9AF#!g;&AS&eIOa;Y1*%m5$7q+{q z&Kjo06zq zX^T(H@hua*dmXT3@`!$T3BP2RsN;G)+xo{H*k)!ZRmd#_hVzzh)kA;oB@)n>ZuO@) z`Ah}(`)Hi`Krf5r2coZqH@$Kx(2g^10hcn>dA;*Oi%1Bz?2&)Ua3 z0X2q)A6HFQ+rR->MeZN zTj_7|wn)vBf&>;w%ZfK9y#-RCfc{KwGJ~y!ZujFXDjD`;tc7#ASx^*LSF|Na#i;vg z+tWWyu)90kJGxY&X8MLDewJ39DLOa0H1bGe1haGkgGKL=`|gt~^iW3!^la|+96^eu zYzFm`A@z$jqI#^W(w{fD)(+O@#C!4Xy`4DEDBVWWGOsfCZz<`c9GY8-jbX=zo(jH zm95 zfquVAfaCpgRP}DYQQ&90D+tIA2of}*pC6x}AI%SFXnRCIzY9MlClJUb5b&ToXm(Y7oTO^E4)1F zLJ}%wSB~dTOl16T`PB-Lj!Qn+Wcj;feY^0g>>4;Y!b!`PskFTl?Uj)Y5qCqb`CeM_ z#^d%KIKO~tyC}m=W#?0(bLv;=A^mUm9jG|dcmx>^OO=RsELTFSn#?G!xqSOz+C<=U zjv3#~JXMxj({s+MOg?RaxE(jAA0K)79JCekZyCYd+gj~cJsNxpyE&0e{T$06d2&E1 z(_L2MYl8YwhYFF5&9yxR%F`vt5g1E(E?`x|V{Lh^!BACmg%OK%aU z6nY0N0DrT0H{$15x~OOq2}Qz6Ke$#wNgFZn={OG|G50zcOT|TyGHTm5rixp(DVGw> z0IqSG>jWbrGBDD#W7H%)!tz}a#&A?%=xp)XGV9YNzK*E3BZj}fq8z<_#~gXx{zt8y zJ2It$3GMuj4ve=Cj#LgoYZr0j=z(k@(}}G5)!`NT+)LrJ|C@HQbdLq?aqt(4-kASEKMhSw=eU=Z&iL` z%V1RNMYVZciS|()XWrshxIyfY_-%Cma+<<}77a}9Cr^-9R@h*JDK&}I2FQM z+}RU6?!I;LbvZ{{H%GS>6B%t!C`V{iL3j|5QxI@)Whi7~Tx5`S5RkRKph9FsWaJjS zudW$07Dt6&ZE&=oO}|De)-**Q*_D2tPlJDYuPeIobN6x8S>pPX;8IHNbSMWhDUy}v z{lYLtVSj0m0_?Rkv7&|D{mllK{cijXW&3K(X5f6(Dgl{jv?*)rP4N+k7K41 zWM9gj0nC{BrQ=Hh+{?ko)=h))29(LA3x@L*>D3w}-J{DcmpZZ2r*wuLWa%E{B(;c7 z>OgkxZ05#MMXA zTBg}xb+>7Uqb17YY<8@RPMm7Pah&w;L-3F`9R`a%raK4=j8zCTD1o4Us91i)000=J zlXNbh3tS3(B;D}PI!gjhI;p9&jb26tI%dcA!fbk8=%B5gTR422RZNF^#yD zIrm;fbEpbi)Lt5*a~8fzaeBDk1?03d-Cnz`r!zG$)w)E6G{G+s02qeGL_q^I;vBXd zt*oppEibP)4dSpYC0W-W4yQJ0lPEo!WBmqDh`QJc8 z^n)@+_LI0`w43X#GmWeb9&lcI@{ebSl;>&>+*%hi_S zfCeNYRY?&JLA9oUFK_Am35`Em2^NID7ij5=e~zJU2nJDxG2p^) zsu^m4!GktdNp7A_EvZ8v(IamgQyg+fIqfF4i`KFcMYI{-WboPPnK<3->;7i17MPe= z@F)hRAS7zRmP;+W)#?;fY}i>E2(TYnYFt-hwW@-&2>fcp85+UyBrjdAE(Wt(osyH( zyxq+hfO-iggxiD%6BKZuH4_OzGM$YigvweyK%#}BsJQD$n7F;hLeY{?kVz@@C21*; z=K?o_5rs{$8wF%skkT=sNN?orQe7e_v5WehY{|2e%pgDFd$hRVN(Js0~2#vHo%W}D5?1K38`HTbtP>-e*@n!?x;=XOopTvqmlcO3%yRMz8?S$QsV zr#__IJTV#$5+EL&kY5ccmxsW+H4w(76{eCmW@dfWEJ-1H?Y#_le}?R52dhS1bE2FJ zh85~}t(MUB-Vqt$nLad4d{%b#xz_s-L;d-E+8zd0Qe3A2<4XLtL_tAg^67p{+QD~Z zUVnSPEhj?_;)H%dT*xMFL7@U?DG4amDi4h7hZBWeQsC?MLLGRiLS zY9)fvuv*02Qd1}jpt&6uXgWdB_l#i}1%-}qD^xD*KvvIF zEQc2RL%)2Dyhub&R)_a4$%+BSr5?Mo`>4zQ4@3}HNS+f`k5s}S^s@OZlsb%>_7jxw z7yIwK^95bdKLwE^@(CIm0#&8?__s-^hJuLuUBILqo4b0;P4PoP2-6=`!20Lj@#S*%IT=xPvHu>Hd#Ej~ zEqmts5yx#Add;ozvKeDCZ-uwxtHQ|oN+eB&E}*A=P6?sam@QE7B)UwR>Kd9q^+NFY zWGaGcFN(29bp{2SWn2{6Icuhkl?w^gACE>>(1L%u1UmNxiVdl(2qH$#SX=Bo`S_;> z2ZzSPf#vu@33GNs=H|tsGu@4l2Gk}I3tDJH2m{az0s~M40honr#v^fs!ZywED&m`r z-63U~8WYm01la2QxN!N}`AXBb`G{8_Sv zh2C1(H~|g<5_Q0lSe#U%H3^ZMl-*8Df}gvdp)-7l1^U8ex4D$YxR>0t{`TnG=AMxzbJd6#ZLN$~pcr=m3 zisX@!5fwIpD<%`%&OK{RH6!s5-a8^@f9Fua{{54TN)qwlyL-_m=&vg67qd|}cgQ!h z`go+7s^Zx(@#FB>q!3lxEs?sRakC-(rRY4ehM>wW`E!?tgyL#=+BdejJCqB`a8JRN zl0$}pp&Hmqdl(%ak5RiCV6heS^e3Ah;KDkZX*f7NqbXUz&_@ke^2i|q8DA`}KL;ax z*~K9>P2r6%zf-jDIvg27pI;hsRw0KdwS;L|TM+W?w+H+7Ljo-vX~c(}+LrkqtkP0h ztm^Qh3jKim;%fp$bn8+=W!ddpc%-=kxCz(Wpq-W+gb*De*o6wSm$&+LZQ(g33j??7 zSeq!jXPy3iUNzjA-S_0800mTvqg>-jxkFNa2_60R+G_`&kgUP=O!;0n0s};<%_=vR zl(ml7YU#+b_HLwoCGcn}_%P(IJ|KpzoOQUQx?lr8(Fda?T=0cruYO?{XuP3Bq3@!0 ztlxyz^jlAKqI2%W?_>J7O&?<(jFPRl`_VrP5(JMxb9sc#VtVOtFS~#H>gOlAxfusK zeHIpqVbL!*)kIv0GsP~hBGTFDzc%w&Mk6Z2W`hNnQf)bGs`YSYZ$-;kAXr8f*C0}8 zG}Wkwy}*04z8S~n{J5d5d+zeZjt_ra+RXrOM$uTctL}0SnjG1O2;(xQOewc;wHI!k zAmqB2h)1!L!ZffqM%)7Bd;HWnS96%;qZA!qGks#2bv>MUID*teykuvQSefZ9dGe79o`&>Vb)0qvJb3 zQj(^t6FacThn~PRcA2jk_FCz6H=+t7`239A->a&X-e|3EH)VnNq}o|2XLoO)5$v$8 zC{~l|bG{3o@F#5iNWZb^*%qVneXh})Ic#$8E+y@vgaW}c7}QWW$2d&UF(Gy8lCq=kLx7F(~MebVXprXf^jVg|f9NE&-^{#UElJ+uOTCU3cOUzFlk(#F04^ezqZ7VpH|axMCks^~-IwG0 z`9b$VBlA3&vyU}a;$wa4m80fU(cPAq3<|3Kl7i7UwQTG;cgp)7=OBMx+i(s$2Xa-# zeW4AyxS-;2%4(gAf?Vik&i&C^K>Ye7iW<}SoSBc_AhClXI|(eKloeUTgY~Gs#*|ow z>U&XOgA%3VoMqH1CobED%@&)|1f_fzT6e-yKQA5!=5zNzv5lyxl@PaG^Mq(Od%^r@ z>rL=yNlht5YBGu>c@(I*-7s8r-tnsPTRi4~19G#uy%3cc6SsRgXp^WNEr?uIF{jWk zKVtTfo`;wrroI<#4v-^9Xk-<&DObmKS5nz1ViDqVFm7c``B9efpYy%^SGZ)m6W-n>D$Pm|iEO_L$MJm}mJ9&JSqR55(B0r~&v`q04X1e4``ruu!n5 zsmNu?c9$j^E>sN01gg12R1`EP3``1%Khesgr@WtI@>13?C(MEZnIOgI1JfVNI>!8v z8KMF%2M_A+JNk@)83{??6bQbYd%lNDt^AqPuw!0wg!&}zXx}`sLMI+uR03h)xgQ2K z=gs3vSQ7ml57FxThsxoiq(@+_`BvVA_rC5-0@d)bvdtZ}33qJA*SQBN^&0yj)+7D; zb;SA3f;9PuV2Dm&^pXRePBr7|(;~yqe&kg3H!TAN?xj1h@82~-9jK-yvWp!)__x*n;;(COJLEbf$JuciD~Sde4Rg-_1V3{XkhKwFERJ zXVIjo0?F%;%^6pd9XmNLc_%5eDt3b7T5UaJyq+x2nov2LO@4R14t6O;`+C;Wal)p% z9xJAgIr2yrUO1Geoc)Ikvj_Ql(_P)bUP>gr)$@~l&m1O3QjUoI4}PtY5byHh^Wd_X zN5K-oXWr}Hi#N>Cckd}CC-WTaahev%S2aF;)~kn9a>yO{*o_+8W7<1x3i-9i^8*lR zu5QUc_okS)q+Vx=9WpJWnSVPXa_`fc3z-a|p}YmhQ(Bpv53OrN9A17V>Z;&a0Uglu zxzZli1z%nW)tYlD+Y5dSG#V}h6czD7fPDKsJIk;?;_w}UFE;p?|4ELZKyVIi7A^>d z>`9T`&y~@HP~Va_@TkY~Q=kO-o}g!PTbYs)a+ud_tUAxt&wlqQ^}TY0kzt(=#&dr{^91#+mU=O~{jB$)F`rT%;s(FgCbDxR=B-z57h| z&8ss16OMzYCg+~!H-Z`tjPd@1wn0D4^TN17Bm$p%>C`h8ea?w972Vqqs}WFz%K-4! zOrGbG%wfJ0kalng_#bC)q^0*m@Uc72EkuVE;0s&FZanyqH5Y+9ik?zTT6(~WlV5haR%wATOz@HS|F)|+|EbJG$Tpuw)Eyg`F@!CKAu*=Tne{1du{pXIQFGO*0fqCvDNesP3CU&OC9JA zyN8U&vea*ud#AA&_3N%mO}ncpA<8V)@8m{)LZ(Vs39LWs@P_>qQyWuLnbteG*f{L( z)KkY$%j@yBeEYk01N%@<^YRUFY%sVerTk>G1$P1R)AR0#2g^?ILNlLPsmn8E=S7?j1&<0Q^Yjvz}vf3s2hmW`Al`ZKNrg;8)i2zO=aGse0Y(>mM6Q zx3)hih#S0$6%RR#w-b-d1??cwN~X(dQ`t2rX_j)Ea+`*YblY_p*yZ|~%wY*AaxISq z``MUHl2ZCkqAVNcqTA4Ewql&Eq3FAU^*1Cv@TX0x^y}z6q_+B{RO!F`Xo#AdeU=dyW)lvyTyLv}Y)>}_ zIL{|`r|H75ZXp7h-re_K3NNArN95(2r=D)o&a>Bgd#PQpxw z*q$5D(hH<3S$@E&x*Tz@--t5$>V#wjmGVKdm1-vA_i4MF*pYnit88G(&8o;*x`BCTTYgaPWKAs)W<4J^ z%;L4+*&TyO7JH4M9~@@B>2Ip{#8O!MFPy_hJDFK~SS4+Jlw*QaRzqRdWM1|<@an1% zYgcBEapj4JQvBBFKwV6<-LM8+sB@L`Lv<Xx<8q3ESW8oWkt#zL*^Jjv>8T|8 z`&>_Z7w<~1cfr(cx-Dj(hewQ*;XT0Aw%%*Zme`RAsaaZvn0#`+W11}Ynrfa^ythbq zX|O$(t(9P&FEtK7mgLQ>?Ap+j`Cfc;82qr>(=HA!r8{6V%cr`&&_-c7wm>-GR>vmi zo(oz2kxV!Br%NOS(91eO(m`gRcDpEE7)8yS^P{PThDAX*V^b?$U^->OCOsCVm0$#A_7AKT#SL$*N%5uZ0#gnPotKkNgsINvS(;pyHN!&G5 zTkp*pL(3k!VtCIpb?MIE%OFds-eM|u%yUtzAJ2rT(A#lv6K8yIYOPJsG2_H@3%WJK ze9F~n!0Ty5b%MVO9Y7fhBz6(9{rV9e2>VHHmE4bvfD&7t1hzez@&9h#7f%%VTTBg(cD|60c1b zx5#nB7jp-fmdN7Jd`=yw;+rr9*?J&*s^GWNK^;LRsA@^#aY3xw%2ma*O>?e>A6VvL zWBTnQg`|sg127H!%QaKAo7EyTyz{|!HOv%^&oi21qnD%f7|iC~ow$|}lG?yxJI8oN zBKJ(hh`WPM-*Jqc&|-Y5$(z+hT3-4VQT_EL2XlsEwZm}uOVuZRy>hWZ?mat1>!Pz= z@FEWzgCPWi`3Uo@;t5Yeau~{Yg0fA&n>Kgl-gJo z``s^#VGPCZF?f}IR3`n|YsfRFyLyt#rHFT+yZH^gb-dBRl0UTA>0?92>*h*IKt{E0 zN=wkQj0~EZyn${PW&tPLqZcCVS)vZH2*Tq(ouh;MX1rEFuRgwnPw$hQX<5gdH$KcE zJMu21oWFLO2G#n;FCjL~owi9G|GP<`UXEC06`qn}ebp_jz>niE7Kedm18&14=)kR& zH%N4T?B4GdH&iOni+|1^g2@cZEGOtG=7wzKx-w|r{zPHUttdF;hIGRB?5M>B&Y!}X zvU0XYk+Yes3#=CpZ8em?rPj$8-LaW46PPX#_YdWqp5mo#JUtt$Z3+!yN%a^T&d?ea zk(8fv+=aXttZUx!jqpqKM47Eka^?A;kIk=4ycOfg^>NHNvlDb`@D_+-8&s?&+CsmK z&o8ZKR#`J4OArGArZCiRIg0otpHGB7OB#F=ep3ylcBY&*4dPROnu|>uWK02neJ&X) zAk)Z|jZsr|=sI?rO!|_v!U-;XT$PO5N|pSS@W*`o2-`{5+=5F}L>MT49e(3<>k(6A z)miu+sV3s>#I`+FB|(US4HtTpB1f zL%Xoc>V# zdAIxBjhtVNpq__9Fm!0EqT1v;LkFQt98?BhE%d%YPI{cE>`J=NJP-29XT2F<5~yuA z6rkwj;qsa(97py$>e$5(i%h&yKyq}cYCZQZEF6$1fFvB76dIh z{Eq>8q1>K6$jftk9^Y52bqiTrV;8>c8t>Fnu=W!+GI^Ydw^C(NIths&lKp& z`Vl$8G+3Lc`7SLfR`E8D!qeW9t#Jvoe%<@->LdyKNjz5rZ?L!=r7&wz4G#_ss(=aK z)N*G=y!%oWUnxoJ8Pp8RI*BXLWYzx&hu0t%TCNJSqEHJk8bUkLQoW;&>kGl#X`g$|*7L9~@X;$eV7&;|jepwgZ1GyZ@p|Nv-{y~CX{Ba3ULg~l zuU`J>MFGAFtp}31^4+d%<|-N%1Kx>(9D`DOtHN|)_M@mrH-D=wxZq_18!MFSmF(vt zHZ@Z=p-5)c@itCnQq^2*bFnxqhiBf;%dBM-5B;t_I%d@Q+KySqq^^}x!0pRN$R(W& zm)prSK0jAuXvq+YS=sLpz7IJURYILJn>lo`N#Q)zmU^glN~&}lw55oY&AjyXS6r^= z)D?TOMQh}jE4ho*c)(H%utpa*OUqKiX6l$@`kHzXULdu?LgKqZ0Rl)9zmub%8X-uZ zmJ-(-A@ppQRW!n8U5p|VJj``nTorPPE#%euh;<;(u1;L|yodcE1mB+EqKA z3aiL8hG@^q=?OE;rJd8c_dYp$D5pdagw-mIKwVd}Rx5PCBdgx=J$~gg&l04eQ{9gICu@ab{>BPVOJm_Bt^L;O}jl$PJ#`lH-VX6Z&YNnrVr8;E; z>v6k_?h)lvEF9nGsJv-c7VTVd>^VeH6oIH8=+)I3m2}%RXXZ^ zB%F@HOQ~Q*E#O8n4zcHNJxn1tu(zqO4E1(6!bTHM74o$um@-H<`wLSZCI}hrt3g3~ z?K!7+9}S^d#z+ZKAhmnRZrxb9V#_7luyTj$BVaN#nNCQijBiXwbUvYO_Y&ES&&KTA zn0p<4?<%NJ#6W3U%sY&`62Gq%>7g|fMMHg)2bM;1(uc;9Lw&F)1?Pzk20fPL<*mU2P^&EZ zw!ONEaZa?ahTBv{@rz}0^cOyTPvW{CQ9ry~homx_x@>lJZObCh*}G4+c#nxbGJXC= z%ERCOE%p2jTHqGf5SHH;8$CP*YQy6YuFL`4n>K1=gD~?1Ubr)2Ir03rup7)ju~BX= z*R1q-l@J}Oqmh1|A=CO7`MMvK-7SHjur==a!DD@=>&KEvae4aEk3wXPow`^w@Wn2I zLKW)N>0a&7rua%gF-Una52KS6_EL|Gy|25nvI!Q|&a1fROg_A*?PuS#eU?m+=#VXj z=ILT1o%|0X+&-$hSz|EamF2g1>j#F~bZAd0Cwc822u%o1%~}VmfrLtx7VsJ$CQI=} z%D}Y(O5IPAiW8)vr+~%rYHfky2I~oea(nG2MEezENhd5xi}<$wFd1(Gi&wA2YwhFv zeK$2#vr-MPSsV?-=NlJIGgoGi%N3wwsP|kXpb8R=&y_AbVNXTOWKj{+hjx7Ftk?BG>rqvE6) zfbhTwT55OL#-L)KPIMUQ2!bNdAJ^{c-gYEy8cj}b-X|Se+*GYi$oOM(m@aBwBwYP4 z7!T=H5n!;~RdD=1a+7}cX&AB4Y9G#TEv@NsIap>TasbW`#+|q;)S&fX7;dP|kxlsC zE;r(hwmq6*7on2ghuowvc+&$&7uBpPxk+!zC*qJh7Z$?mv1PLIh8dn=7sb<0wH+H? z>W>*o6Qqa;aScu2mNgy%L?ux=9w-KU3yoB%by=NIXY(d)*V$le<-D;%`)!c20HT48+r7fxvRVdZyqcjwodpR`K0y+< zN~=%VoCyAq+GF-~R&?4KDtp6jyMJdJ7?71BX(_AEI_#@ToZJ`DcImrRXM%px)`2uf zWt#4*>Fb(v!@yQTO^mey8FX_*=T;RDA8^x3Xj-7FQ5}T*!;MXDEf%Y6=pxtW&UA1W z1<92EQ>tA%^CGQdphAMdbP#d?-*S=nA*pM|W%lrw&W_KK!pG^>?$zH$t1pB_-%voP_n&)zpX~L+|;L$Gza8F3^FdF)3556z@A<-{? zIlkuT$}>j}sVd1g>i)1o_M{410kLSEbxF#Jl*KqkC7g`pL*?6P>`+lJC=G}OoD*Fo z5eLHRIx+~f?GHh5>`WuGnB7|dI>cCWiWk(leJqe9gfJkH+v06R54IQ8m9I7%DV{Tk zz-Q=2w;)Uq=sR7&Qgi)8G~H*CK!;9dtPAR^ysDcv$f>JLH9N)1wt2XPV+SbtD`clFQh{t+hZCO9Rsp2$Qs(jio)iFuD$`oKyNmZF+gRL{jLd zu7LK+H8^f}E{lcy_VpOyi{p0XYKpGuN!YndEf^OV8E+ayfy9&3ip};S1I2@cAJc~g zVQs|y#jzW;G+!W=DA{1yDAPT9g9>cMW|`Of82a4nd4$JX==DKmyq3~%%57$Rz&Lk7 z1VV_4-pOLJw<`IPByoMn;G1W$+7|I|WLOu=aos}nATibi5_6-r}vk?n;**L7(6Zz6n1 z+q}nPAlW2fA8ELf!eaM4&yAItYYvsyYUs*g?ZEkQPhhTw64jGV*{30eE!QfUf^Eq_auoU1C4Rwe!k5jo4i>X;)Rprv)G;|p!?zH}r zX!(h?{36IdLvlM{C~hs+@GJvN<+=rZ6T#BQW=ZE^5aVUDn*SdvNV+5!z*%;aja zk|1)EBP+~{YMg{{Pf>9D3y5+F&v&C?fdBZA+&U_G%rKm4ncEjXA}Uu+QYI|J#r|-z8g>9~1Z?6`98Ln144kG^fUz zz)WWhHoQ9jVrUlpA{#m4^u9|QfXiJmrr!Q9ap{6NK~z|rgE2V$BK)F8~huF@SgJWC%?=yz2k9l=a03P{>% zSxsS?>(G4fOpVDl1t2?m<=C&UY@@a$&8^N|Xz>Jjju$WRdpoPqL46q{Yx}Dl=z&Z9}T-2_^M^8*>vtEhhG$R*J=IBoXL&yM<`@)Car~DrPhCq40 zzV)iba_G!`JewT|i4Ym~D+n1iLc_?qW?COGpZjV>kc`KpD5jm`MF4&5@{QHbN#r3Q zucxOW8{Wb91*H_F1aJ`G9fkD=_j@U!J~4I+j9flP4SIjfK_jOV%@nzcIT1b??f4-{ z@QcPf=Vla+_KQ%$Qv@HPIBJrMidx9LEEV+$tAPig9S|D)%6!^34~))a?}arul$a9NdVkP>|M z4w3jV19R^lySi}B2$`_U&jGwZh^H&Qp!mn!SvHaZrF{^_81p@x3k9DW!^W?8t^o6Hd^kK^_WMsf|@>N zBPGlE*%Ar{I$Ls~mw3_^PI;C;!cic>d3VME|Q)k7BELECrX>BrDLNdpRQ+n z$St3^cvo;n+RXFX$v4m1IVv?mjZCH$FZX@AG4LdEd7$(ZydUM1x%@?0%`tylm##+7 zJWs28S#urUnG+_EWsF(RY1jB+Tt+N~YDPfbUsH3S6v1{y9PtGyI4_A>?JKaElrSW+ zCBP}r2@6UJ=t%^5rz+GM0Pd+|RkR{qP?vOt=66y$su9-*h4%`g@e>IqPn$E4Y$}(< zd97mstV`Dy=FVUrF&tdliB_A1EpH`nH}U(3abN(+afT%%L*YcSZZG=scGxY-?*#%z?D zHVAv2hBtmUpTJ=%-99H_1FTbs{w>uWVF5;I>-i!;xn2wQ3rRYt=LfsD zHr^x^8OiTsAfKO-AKY7jKSf*-&NV#O$i=IjKX z_Yf%1lm4rR7xm~1*E#yi{<573P4^@8GgphNn6yettJt5X4aa`8+NC;ITC~Z*!Q+TG zVoU&1aZ^y*O**JR5Wk5cZ`bBXKt~%E1kc|&rRO8TYv(+!s6_;1`U7IZKq(-ntidU7 z1qWXg`mT3*+m0%dZ34%nuSxEHddAuTED|7zjp*+$0K4sUY8`4amK&eDc7X$uRpU-! zw&omSEvEe8{JjLhZAq4=ATidd2a7 zLHeCXab+ku>AisRUVEqgmiuCWR1w8nB#GnodaZ}L{|Hh!k~0B))n~&a)s6ZA-q$w- zfxD96PU<`wVvORyyvo@>`|;nTDdq5$rd!{pdyth;CTcr+A}^TGvC5xz?{_77PeOm!*02+$B@??$!PJt167 z{++(s_5Hr%aeYv0Co}uj-j654=uFbt2J4`lj1OW-G5rQE1nhDA!T@@Zuy-P1hti|1 z0jPMZI~ZB!0Sdz?Y7~+H?6f0P;+C+lI+mXB6{`7bFD6x*;0UYzlKRzOt8?RgVj%znUm7w20Wg3@6#)|F! z+AE`n^$}+Z)ICU_rTKS~>30X=)WF_#62hKd4k!;Q4}C)3kYc;=61&BsT8Z|B-8UuT zYC^KO#nxrb9gLN~JK*-%J$Pdvzt+?WEVgG5uWRoLNy*6JX4S?$q|z!YtyW3xmHd?e z3eqb7t+Qu<^Pd@4z*Z4ym6cYJ=;&$h6$R{$+~4tA?ZW?%t#ST^OixUv-m&vMdj?=B zcni79hnc*pkR*4ADzHut@>UNAd1ePE!rDTKcY1)nfPypukqS8*gO6+HPmW(}`{fU)e}6e)W&42(E8>r@AUMe)+Bwnx28j=x=6e51 z(h^9uV=RdqexGd4QN0=!IL!w2(S3kkSYfJZf#%=$7X#`7 zkBs2_k^zSa)K0GN2q zIMokD6ufNeMLQzB)ax>_h`5>QQzIy3diTHCW0w$7Qx@eH;?=CNv>SSv&~V%NP4);H zKwq-Xz;?9%p_z=Y+4$+T>Z{FXbJEOLXAfmSWG20)CM)&!P5?&m_u+xHe>NK(CZ3N8 z1W+VL2m!4ZG;c;S9ux}v8*!J~_4!5roNGYs%NRQREr>M@Qon`JQSb?`s5R2^Q21Wp z`pLKCfRQ!)Y%hV;IoLtu;ph8?ED1M;A;&kDr-w(77^}fGq^t7s9MXC~(_v(3Vg-%u z2V=muNLI!Zf>Dt|K|YVb6XmnP1KQH{qUop|$ zEp$RCG|&rkZ6j_k>x=r&7H0Px!fni0D3ae$#ZB|`DJ-%%?Eh0LAq0dUF7_{7{vu-C<3>bnjh`?#)Y%7lT0esc)XgS_Zgxzlh=0ndX-uSyOrXulc{A0wNKDrh9g6c- z1s%eiUo-dId5JZ{CVnm-Yx)&v(+gX|r>p1+xUz?h#*+Q=k3L3VA15xM+$j$94Ldo3 z$aACpvP7xq^yWtVVALVAifD9xl2=7_;W+fo@ruJ4d0EpJ2)yBnIx=i0)Vv&YecE@$ zY-aHJe_W0y{a<^1p8KBKiENsoUl*A|;C)8;CnABaZzp=}l5<_IPD*KgCtbW;)5LYm z7g4{zAKDao+c|20LK|lNFL~rWumrHw&f79~n9c!0S zoy*xSBTM&y?)Tpp!SM!dU;Tfkw#c|%FdI@Y^S#b`H%C_X13m9kG-I|uj_Jk)q0yqA z%~n$CDTp~WlPCTeJ9Oqi{)|1j?N(t&AEQa+IISJ56QRTxzcIH-P(b|TKN$C5o5eC@v}y^FjMyp=fCVkZdpO*>zBkB zmB;fHdvJ$F%>u>zujBn7S@7)}b9{P-G{{OAXC&M%m4Y{jvYm5iud>6!`KE%BmFT|R+i5+9S+xJR9(0MbtfzdF_KKjgp%rn(vcCDH zt(#;=acA`dB4UwH_W`4LJ%dO%{exhg zVD&ms`eKn9ZLtAvOxfrQLb+ZV4E*>V4RVd*I~Q#ub-5pXD8L&t z&*f{c7L&U5+N<F$k#6? zPC|q!iqOVTP$wKgbm;a@z43H&=){i+<@E@is7ZTYCJSV!w>3S(i$>$r$36VRzW+b# zGN6Pf1EQV4^!aq_xRIkFEeWnjwy={s0GB|1*6i|Dgy++5mnI}$GWDQfNYg-nxI=r& zqMN6A-8VM8o!dQ=;%o1WSBFVuHC-X+tbSHFrpt-RxTB&)r$lw-NDzTT`+5o99) zZF(f4$-8bL8FIcDDzV69W@sn&My8e7PMFR#(KGHZU!4%h$W3g8V)qNE7BsJ>UO3cE z>+CzkLKH$Ik$1N5eamJv9y4`0w0RqJz^^foRRi;ePtBqsy(u!y0upJ zo}eJIIp`?jamNXxc>X`sk^^mAQ%O(GY}uVqOOH2z=hDs^vlj}z$eouBIphju)FDP zJog*_x&Q65KJ^9e_$F-JZ5r%!lKr};jE#Sqh{!ktGOcrQD}j^%Nw6X-tY#Yx%tewH zz(m#c)%g0^1IEyL@l%neyD+NN?slZU&1WwuF?hw3FPkGq;xOB~qOrmyKuP4$Ni@d0 zBj3{9t?LLeY(0>GXVL8D;5$12JbxdIn+$2LuXtCe1004fP6U47ZnJTQ#Vrw5`bxZ= zID8;Qd@Pn591BHA`Hv`#lsS5-?|mmbI*6!4@QU6;now7PoNW{-KmwH4Y>WVei0rX)NI^E-tvAN4Gq&z6^yluWd)#I3dH(w_ zIs^bCGY2&W02&7;>Er6yNhGD$&o&Ym}pYt2&7Y^hW4b#0dTg~ z9vem+Vpe-R`bdIFWXsHkQpZ+UB_`?HKw5eE4&Yyw@{$4Pj0=(_*B#DWQdD2l7vxzg=I)Y}%BcGEyW0 zm)WM7K{JO-88fFWs|^9CLxoVl{}G60JPw?cMh&<41R*ltZ;uxOZ1etjIf* zu61^fSU_6ho#DV*GNno+qA<}}Av6rr@w!`!Q&AGimQF$k*^6@lfK3&cC! zaj~%ZqWrxo9;CiB8kgWSe>z~l*s8(e!mXGOMUVAb@tlJBd@1Q4oCSd z`nOnJMNgKMrcnG>%BlCTfkR+NZ4v#RJ!nZh}cr7Zn zmUi56j++gvD)O5p3I>kE#B@ATko}46a?f>E2(wlGB&_)MGcL)PI?|^3K$ay=To(9| z-GDrK6-GavjD7gn?^|v20DYe?68i$IgoeyTaSlb2USMezO~#h+|Z&w7tMSe7TdK{O!}I% zYhj|lIE5a*v=7PhIqp>wTUg&y#Q ziZ9BzRjFRfkYL`nBm6;bTW?oLWqdh%;|yq}yOzHw1sD7csFCfi(Z517GglkeI-fsn;F)b)?k%@^j zR(fl_>w(0+~4J>`HYvJ9}-JI!j^>qEy*xRx@3f|Zs; zI|Z`CrA{fA+%M1_a?|(+3OE7{X3bGUvtM_)Dr5a1x+=!n!ugkvknxyp?y~q=3N#bk zbQW1}$#raS{w?ve^nO$d6gcDxq_ng%f7fK;`Clbc2@dauKlSB@^G*CBg||T*ejbY6 zf36S}5wsiU!PeANqo~2eT@>p>TMf5@>9A%FRoA|t1VnAoE)&o>b1!y!4RM3^L-2=% z;F$9Eu=@;2y)Zu|6+{5oO*JW4!k`#|Q%eR$fnJ9IpZ;HN5;6x0R`qqS6g72}G@FuT z^Al{k8=)c#StKqq@Eoy-B(vB>*IVVJGQEFZ1&N-6((86kivAqqcD{_ES7`aPt1gX$L*Y` zQ$sY3R}HMa!pz}9`(%jSEH3k&of}X6sWc>I_T?~Qs^7lsByO9tuC=EQrJZFwD;1@e zCBHqk7MeS9R5}FWy=>#lmqoxwUc9)^LTR|Or3xUQ9rf7+53q*4i?)-$|L9mDBWoAM zf6u>cxM=<|eT-K?vTi4|(Z^}Fq56dXxSLSkjUKhH?8&taB}4uWb%_Nbt|*(6 z$C+Pjf16zT*2yyZ^;y$a-~4fXA^PkfK&t6h524m1;E)SA)C&W+c?*=bo5iU9hE%(C zb=8o^6?He8Ude4aQWUM`dRv)?(eQ`X1>?c+ff#`lY5St;WxAryj+2s_FK(5mXe)cQ zcgdTVjolVH*+KZ&bk6y3DH%!N>bItdK?h1(K)XZ(*g| z=T`l=q-=HRq*@h?`BmB~zG-jN6}HlMZzXGcI~H)zbLEXtpdID`?ojNw-#2#|NR|7z zYw{?<)Ft+L{Gcf{=2S~lz60->o98IyIH(GtRFF$fQIH~_gR}e}f3KNi%kQ_R{^RJR za@_;H${ri_6f|I$D!7%V3K2Ga%!s(2%DD}^6?vb+=DKS@Lnlub>kOp>VJ@E~4~zSJ z=}&s?6mV}#pQ2j#cz5&N_vd+9$o87e2(3JAVldmbLk~M)!<_qA@8FH|Re)+OwI#0{ z$oEvH;2{>P-=3;@O;#E4dvO^%%!T?3+;DeI%bzfs!3M@xTjsS>d>`K+9VWJ-d#3@V z!pt@B71ic4SCD|Fd&>pyS4&0K4<66j&ew84M?1pqfIBwg@8mihg>r34DnHd|NYVYp zBQ3Q45-kiXuhX}@kL5+Qk8LjyW=SmuiOnWPh59H9xF&tPqr()N!3$?UfrFir`k5i8 z7WdhR`sepReK7ZI=M~P0pSWv>VzvAfRob?nfY`#1%dM895?X!Ol8A?Il(?@E_q?r1 z2_3-UiTu{&MqDJp1@9a#B6b<ks9RX&|NN3~=wBUO#hs;cIXDv!e3(qEi1QQ)fiGL_Z$nP<~1EAenKo`0LM zLVHG|HWD|>hs-$r98nXm{xMwn4&1h`Pw557k{59jdcL%oi$ovj1{G6o>K0`BX7lm; zcb+zoyz|;`?$qs8*QJQ5*5t>G+kh`e-fSZ$wf@E>cj+In_lb z^Kxaw4qVMmn7P06I4Dzm6SY{6c8M{w*7(q05&K?LmhG#OT+ODw1eUWE8NVu|nUd~} zX1$VY|8CwzY?dYN5os--47IQeVROru_Jzo;eO5`xy!)G zWw5ZXhQ^dTxiTZL*foOWqL0FeFOkkkw#L{r;ss4^TVg$FfqweV0}kl_VlYf7t$)Tv~e20D#PBoOo6Xz z8pv}?7f1Ry>dFOPI4WFiX=QUQd`>V~8k$%s(36;-f=FU_=c^CFmpJX@2JYv?jSYoR%#J#1XDmW|Hfo9F9#K?UI zwzx^x;FdJ4OhT?Vv&R0{ddVW-zf(>w7WFN4z1h}3!Swj7qp$mgB5$=X#!f zNI&_T7Ol+K!nR!b;to;CN>U1k>DR960cmbJC8=QU#4cPDidRzmFcx&O7_@ahS+!z= zyi&~C_&n-wnsfB&R=?}jb`@F;4OJ7%=i0~9nZE7`pC`=WJm}u#i|u~ws9UFz{b;?; z{i*lav(Ng!k7f3hxpCe;K2>pV1gI63n;kNe5N}Go%vX?Tm)J9YVb?Lu1nWw(AL9`z zC`JYV002&aG6D({kPlOcl^*~=RzL^<00a-qQdwmJuK>gm0A0n#O0KCHHJK*cOx}!r zOQe`WnhinNr8?juhfu^z@6x0fBOy#9%PN(ZB@wa@D?3Yi0~(jt0*R3+CV8ZfnN_q~ z*RSzyhz$ zQ7s)rR=5?M`e{{7EF1AOA&2h)OL-(BTp#ZZ?8FUe=UMJLH4B37x@*(cWbMdy6Tjvc zJ+d_hHrmMrjdxBiI$6-^c`kua0`LH+0A&Ca03ZdYCOgXj5P&oR08=i}FRF_KpMlf> zmLve8irS1hk;;lvJ&rkomlF02?hy1V!950zan!gh-8ZqqqchS(EyJn^4J0W)I@@qs z6p%?=-gBWuY)fyfHHVVS<$>_Ow@j7+^}oE0r$AQ6C2ORN zdst|lvnU&TrpmGb+u75MQFh~+@=))O$g=#mhMhC5Se01%Hzec&Ym00D3SloU|L zfY3+~TL1t70Du0;2@)g#kRJhxv`Lk18YsjPbS)=vZyo{)#Q^lUV*Om$}5(juA zc4W#bMZI!0S(%FF_ndd%D%dNDL}|3!-Vr{6ZP3$LDU@S!?EF9Nh{z*i|I%#urnPj% zjiY6>rm@jSI}P$|bP!Ul9GbF6h=d86%cNAqXXK8s)6>=sp(u=ZCrwSp%Kc2?UQuhqmG3&jDv9vNOZ39=Ey;f;RdzEk2pu|hoNVg6ok zI!~R*cRX*Q$!_QC_0aJk*|wrGX;j&d@SS6h?}S);+?}zowiy3#kN-l~43Eq=X>Deb zdBba)ziLfKV%zI-EKl)>Z#%!yO~Owd)qq4E{{~4Rc=H(Jx&JOiaMy}*9^yy%NGOj; zBv2p%00D3SloU|LfR#^zQ~(A*8UO&D9G`grcY;I!3=ROql@$eaWI?L^7%Nc-`WlDJ z+}khz7pNlomgYGW4=WJ|EEvRbRTCIl_EWP(`G+}e!4LQ&@xryX*Si#fCQK#Dw-68n z0000pL^dM;01st1*MBqn+**wput*4La&539vpxuod($-44anQ&P1u%67n?1FZEYfd zukqi&un+)@%pBAh0O%b59GqTa*OD|*V(BjM-TWb)DlXBHBBwQ^GfB2!&P#HfSO{j? zbA5slwbaHo6GH-x+A5Qt1cX#tnaBi1r?ys*ho%#OR-iJK$w@E#mm-vGK&%n=xtPR@3imfJDuDKM% zcIc)_JI|I+WMFxwWfuzyJ)mZ?s3oj z+O>>p1Fr3RE$i_6d+}rm=F9R&Y^!`(maCHKVI)2Y|M!^eXjm+yVFo#2LcmNVhpyN` zUV|20wSa!C=IwtmQ90(&!|*qZl~M`b6WN#Md3p_z&9^u=N(b|%G=3W>PEJ?8W`dH8 z0(}H+=eul-Zw26ae~)RLcQRU+@}M&?6(-fWK!A?L01nVUwXMW4Mvg)@>vdjQ+{lXet{JPF zxlf3=rjL2^EFIqcGbFu3eBjGQR30Me!sw3(jj*N0%tdeS|FHDW&R9ph)gDIOsRk=E zMkzI_zM3tYzV;~cXU{EsI0)Zt_>rMP{iu;0x;l$^m8iG@KF$J;VffuSEdP>anKZZ%bj(lMvUT^ z^1mqe2R0|H6{KcvCxTS$AXA`&o=9Ix*S^}cY3(NY?v#2 zj(1ODOn;s1cg9>Ox0JVe(#cO>X5@fTq*W~ZjQ(8jEw-y|c+FJT44f%TSRC>%rt^o^ zH4|v@5h%*`?-MligXE_`=$Llvpo6Hr$a_F+DLFe|ds?mCz%r>EvQCr*+fK}z zna4?kN#dtZoPQ34jt84tv*i^n{b;t4B>Io9S3O(q(@A*CAW7~5W{9z^_>Z}K;8~qE zbO#rm&yN|(4#)R<=41V$Y|Y3=@JJrXGN>Fsdm<$N;KF8XydLX4*=c`ME();_syc~l zs2bQ_bJs|Xp-CD8jp5qkD)=^SBg-Rl=r?S^0|qy-G9#+e_{hCvKA<>b_#Sp0^vTtN zSy&72?3!tOyj~}JR?ela% zuXTHRV%L*R*tq#dweBt`w;IXRF_v!*nx-Z3HW5natsAAE9THKVUF$c zuLci5_j{&)xt36W^nyyRdpXsV|1BkUe|7glBlYDDbGIlamJMo&F-~NIU&q$D%Nz6d zX;wSUF)6?w+-4CvHQw-i5J%(og5;)covuXTtmmz?T-NMK0nk64y588s`8%|pVRkzR z4CxZ-5Exic(vO}=T81b6$L3#Af5s=*{E3hqSRsn(ao(^Jm7=JSl{Jnm);$iq%}+{|CYQ z&l09=S3OU~WRYAx)x7vAjh9>pJ>nnx!NMz;h$IdMw&TDX@51|`=NShoHpZql$%_<0 zHkR`(?%;BI!0z_swZHew3kGZ1B_Dh6e2!c8CKvS%X%klW^M29o$k88f#OkdKB#@s- zi!M7$92CS1RyGOu(YG(?zxqz~e*drm5<{ovdY)jSi$bBDSK{&S{Ns9iv-=~O(=IXn z$mN*<=O>OIevRR68Nvq6HIk{wwuUSm53Hw*QMHXwxH{t{w8YC=PKX+oGUXClxDKRH zg*0WqB@hexG0HGYc*IR^?oUoyhL^C3`Lg$A;K$eH{zxv8GOH4zrJUR!Qe=&U@;xQ7 z{3$L5>{*A2@MPkCjo)8>NLUL|sFFQ>LtKBwA8uwsc^##UdK>ES?DfU=4J)$5h|g40 z@kI}VN-VFmU6T9KkzB^*+MVKxL-IcskFA@$ROX3HB^;qX788k)Mg*^F!Ee63%89L|*KUv`% zq+*Lr*856yLzII-JE3+NcATkB%+-QG`?YH<2hLij)oeDtO7 zv8^+cId-82$9#ct-E7c89&G2q57_M%C!A)ax!4v5ou@34heDItg;Lp+Pg{nc`(cRR zzMv6`v~mzKgojPyYtJKOI{qY}&DrG1unvmZBHW-*Y>GLV47$K}srTJ3KC>X;Je)aR z4^A(S^fyufNdRI1HUI!2000c<09Igtc6R^(Ko|fY<5Egoxdo5_poj%0OYKQafq#l6 z>oefxI{8t=mWJyMX*dYzw3~En;4p(=am@GMM_pz)KYDKc#JeQ+zjRv=>{BzpUG$Qe z{R@7LK<>yr{rD-hNs+Z?uzN?N=x0ens5%CrX53V@IUc7?``6L((fRa3O$EV zO1{6P=eZ92!55IA-~~~BRDOg`=ItoZ=XK{7M%njav=2?LB!kl3i|wG6P+yLvv3<(O zbe$Ahd|kEJzp}agb|10~oF2*? zOV1CoWmS1vd0OFTZfx&V(Vz~>OLZ)H%vCYJ;jsS;upw+l<})}KvnxMP`>oJGJV2A*Ge2ZS;{h!~Xc!U&Z;~f+D4p-?x2)M(@e3>nT zRW4LoueL`h&#C^r_)YjG<_i)*OVBVp@^Ux~im58HXh@lOW%rrSZt-~ITo5V2@eLo> zX~Q%M&SDsbAT?x(H1d$;$*mLbFum{(gof4fL%zQuG(t4P9&xROF9zQ7c6$!Q(;ik? zK55`yhf7FL;ne5nQS-KOni?TFoMAeRQiS$oSC{?c)4p>JY^v+`kO0KXLJJK|BA5H} z_ww2?4j9Qxn30S#LMFY#R9MwSqvW-5N{P6DdqcoShCw2QM_iBPPTwRvEtfTvell#s zbdi9m1ha*b`t|>ZS}ufbCJ>=sO34lWys{n_)2d5LTaev>dIO-Pu<-WC}`YfDG~AYcP<01E(s!6E=BSO5S3 z0RKtH(h1-_%8&%WmIMHqkdsrT@srftqfwShvbK^PTGiRYR5wvh{-FeAK~j?9y+G+p z#^2|-)YeHol~Nxh7ll8BSk5-s^fWBHzwanA_H7%S9%Qf*^z-+$PrA&$U2n^Kbj5u= zF{075WuC2mayGTz=~J4dcP311URhGEiH~@$N|MaTmeo!5FdE!aTQz0L>!lmLR`RPY zXoR!v$c4mgn-GN4=$YQ8*nym(VM&{fEFPzGBSKDjdbr729TODvVJ1?dM0|4MNmDYK zVvaa}07fgi#d^73=JI}TV|wd5R@V2Yaeq}uHE_j0C!tqnUF?-S z25hU_@(9^Kzv)7)A>(EZ1ET~ht8oR1{JnGr*k`bA*qN)B#NKXcOlERd>?U&`)}SJK znVdZZjnNmnU+Ui_(3euQLeQllq^z{_w2^I+O$^A<5yz47%IG)D5978xylIE%rWy(* zzz82NzsYJ;(G`C2NJ2>U~E?*xFGdeczFfnvtsw*mPC;Fl#9EL7EvlC|2hQIj{(` z@RkG-ajP$>n6=K~dw(^pZXHI__Yh$xVb%&tiIWx+>2Vq8=9ndwzMy2$Q_-|0E$1O- zrk@t+9wuYEgmleAL!|k>m&%e@gDY(x`TOq8s$iCDGP#dUk=JaoB=z~`yeBIYAE{pr ztc27$;r22M*+5-`xZ0<8%L2iDs87{_Sgj31PgI<|TBOQ!Zec4K5_@u|1CJWpk^mb> z-^xN(qcZ4eTe-~M*%HHaZ|Y0?=bKRsXw)(w?PPplcC<+kla87^NUk8G7kr6200Y1P zfDB#$087PX2mk;80DbgLg3opY1xF=dTh+aZ;{bMN0IaJxwMc?vl7zDOq^9_lWYW%C z;$Ac<0=DMcHqANSMrq5cj;RBSa{42d@-VAzwb9IUiuFfFs02Etpy#o^(9gh*fXV8j zEaPsO@zL0#k3qiv_8Id#BYvRyUA;%-IvMB$s&6cCvdEor_Q)O(!QE5)hv)lSWi#sR5;bd+UhYw9NEWo+SMHbPG?C1@@-vGR|7`sZ&EP}tHb|$MqZiG` zUvy)0roq(>o`Uuwsp^HhUc zmj=3CNO*f-<~g%gQqr_eSn3u!g>B;(_Pia>K6oDsEvo6WX*30VVX|3Rz5CWhZpqHr zaed;<6?V0I!Z(0r#yb+@c@z=3b$kt6XI14>(ynu3j*}_W`pD8ZfD`p*s(Zao9k)JF z?t`u#r29hy-d`$$KySa*aA+qluhu0iBEyGe&|cop!rEXS z$8sIn=t$i+^(maEI0=@iJWH{(syj^>SU|WfK{PxbBcIZu2V7DX)k6w=^k< zbr3Uo>GAgXQTGIG`sE?Lj6mq=vm>$Xb)U86xNAZv(TM8uqhv8Klo})D7vP) z&Zvja{!;upQV4|ts&Z0OL3hN_9ZJzq`66;NFix?$p;SLgGLo6NRHc$RE+tvIL*m5F zcsDhEV?^ST+nQKuSuwJg07@4nU2SI>y`T7@8eDNXJHHcF}yqx5|frGjD5K zv}Dx1d?J5EmzhYF*yq>_bvsbbv2(&NQWSRYgbh-Wyt7LVm6AIHZ%6Kq@|hm&2;3ow z7Dxk$GYIv_yyeQGhDv2AyLJy%PsdEFhyL|CVk?e1nb%}2I%bbQ+`kz*<&iGZbN0!$ zts3`0110Ncpo*tY+!qmsdT^Rr|9IDGz9`xwD&+f+>Yj7VkE5_r*^E-sgFKGhp%Q8) zL3?~gi?BVlboGc7Cvi8XGF~;zpudwk9$AM`$Fo?hECApT5CZ@J075iIGXMY&Wi-|L zpL4zvMrC15lG0;rNC4H5fj1x~RL3kmj$0pW-7W09?sea-l>3eU{)~(P0LTnM%>ls3 zfpYpu{JPOga}tzuiSV6ws4#E)=~ z0fP`d$jn0^APxum(xilj)y$k; zs+gvcWj`s5EgzVZp)(4}!~LJ$|JfHAcYULuE*PR-j6tONxCw0;I2}|ca8QiMg}t)c#%?wC_LGz3Kk%G-I}YBk zP&tLndc+}?wk2^DHibtspBho?jBJTLjsd!(j4h99yY~RAKvcgj6{b9xPEO$O}{-ed)O?0yAqt3cS%Wkif%SOI#ROsKo<^72~0R z^)E#3r})lyK$S9`VjXw%K)>28YFM!S0C!|7PT&|c31 z_OXkO^CKzgoI4JckU~vMrzE320D5y+RuuI-A>f3E;ydU?$71&i9|Cb0VcZ|q4n0*& z`uJZK#k~&{Q%_u22qrY4Zs6pmO3yB-WZ|?a$uq%(3&=?X)xKZ07NM8}dPy5$l#(%& zvbZy%u*Ke3vQclA7180us!Lz1rNMG29zOr+nLX6;CfhUiSyW5Utkrug1#6sjpHB^> ztkIl9%!)cLjlqZqLvYk@$l~-=?K#gy&TzcIC>R46bzsAU4C$U`ReYvmMtmSc{o?$l zVJY$|6Plv8K-H;u<~Ov)sgyn{UQ<+O(~pC?tzfgODKjJZGc}U1mXfqArKXjvf9RQ( zNs{dTykB2h#Cz2a!%tVMsJ{B%y5oD_=@xO<+(6ae#dKNS{6+m9txof$h4g)pXAU_R zhOvP#98){_oj$`n9k7_)Oi$f<-3$u)i?q$dGtnBy^-6R+rEh~twZw5 zWV*I>)soW?MV)jXNQJz6`Z{5P~!T))*4#vLnpI5YI!jq;7V7JZTL zX%I}9=)c?TGM|_a59_O^q=umCeX}s)XVx2H55(n+;4jqF4U?rPkC|^})^b{Y4fSsI zHm1CHcBIg9j%I&_2oivH@&9~>hGRX!IUaB*j}5(g_RKh>{6?L&-yxwL&7i2L7YE0V zPEK$`hnyK1@(&St!+=#*^KONxHPr%bdB)c^&fj|by`Bz#P9~Za)IHt(&09L^y`U*) z&_@;DEZ#6;Q=Hd7JEUbH#9bv9si=WO8V=gH?nYmM(4%>oYEfz_0iah}9 zxlfqbg!++F6{1gpb70&an8F_m`t7nZ;K@JvrjR~=5>J(CZ0k<4ron6I`WZ~>4EI=P zX5tyA*K*YYZ|4h2;qYolY`D3S#!jxBF$>-bt>1huD0)7Ypf6H=LK^t4Tdo@WF()7e zOS3ipcITe;gS2agA+6|LWK-tolGBUB>haul>qPAe{r2S+xuoJ<>GGi7SjS~+yPT%o zQ9cV?Ze-G`})no zfI|I&+4oGO6z8uP{|>P7!-aGzNnYT1g9@Cs#hiWPT_+zhS4vx0-@Kfb2y5SgH#cj2 z^^tGMaGnXV81DbqRfXnP~V3-zjwnzE=&EKcYzEtY{B z{KO%b;|bD)pucvjgiko%eM&ojP;sEC%0hg`k(waj!&?<;54Y$ z9nJS|49dOxS?+F#WJl^m+B$)W)~ojuJDJmc2f5DBu>R$|LsX7jTM7wJTZFfxOK{|q zaXE*-H{g2C4xLC;Wi3z{*`WP87*<5Ethp=d;79owF=VZym=F`56x;Gzovc= z>-f&6SP_&>TrGp7bcIq|KKuMb2rglw002N7NCVCQ02DDkp9%5@Ir!8}N58uK5n!GdAWmD+Ci&J_m#aMgGF$gW z@S>k0u_eK#n*O$)ha9FF51OtpsQ=p3i)SCMD9|W-nf*Zh_M1}b;V-ydJSS|}$NWd> zFLG7hSZyc%Ks(mrf0Q4RH3w`*lU>I8sPZeQRBry8k(GDKbTsNXtqe*F(aJJqDmi4t zENIZ5Qe_Ky+&yUHPBKa?(~{W@VuwVoBZ!|bj2%VovnP#zm2z?Cdx}MN$=^;jiA{=f zcRW?|yj2@Ju3zgc%#EN&@J0=+p(ZCbIQ|4saAEufrsVDlN!1Ie+e|Svapkrc>(yVy zWFC;9K5FkpE|2sELNCTV9?wCDJKu}4ZUD^8ILa2@YJGz?@XzA{(MffxgjC$6vPrgM z$j}hkZMsMj6pJRjABz_9poCP9L|(#;F*QHivp??)mPb%XpQPs?Z}ZsO?e9a{5d&L< z?gFY24&w<51%Lst0H^?I!x;cT)H`h<06@SR003eesH%SCK&k+z8~`0KOH5KxZiY^- zwJJ!$qgHWmZ;bl}>Y!0hondFpuu?u(l!LmoZYhme4SID;2ajcZ9^;P{%-|0_s3QB@ zN4TXbaKMO)$N0as?1OYL|7awLYsu6vJ6LW>R;j~k-#gW0qP~}CdFa<{el<7rBaIzj z;teM;n;3G(+QCXDlbenwEc(5vP-XXX0yh?kyQI1T9>%J0s9IfrraG>sO4=W15{q9; z`X1S3dM+AJO@8;7quSBMBk4;NnDAQw8UO}>0gyDD?*J&e7$-Pz*Z=?k+5oTm_`v;R z>39W%0E%P)Nq}4UhXrVbd{Ld5t{TNmnCCGF`W2(>tneRnv1Q@~q8%Qc{Tba_L4>^{ zug@~r)T;L@GFU2>5lzmweLOz4OOWNwsrOX>))9VOn2?H3Ze}|0jeH@1rXInTO4nubTt<;vg zGw1x8^~lbgS!!$v^FMS=O`6DrfZrx0PYlcdGmiXSk>^GK%~1c1hh>+YpE%_xBE&># zYu^;C8;`FBtGw{F8zg~!7pR|{89LjZ%wLP#wP`?w78Tc_aD{zEocugaf7kly(c{T$ zE|1+U`u&&qR-LA+nfg+E4uA9ex}j5>g-Y+cnetx#R_^&*OPtKC>IvoOOPt%9y?Oa@ zc^hus2@SvQbP1-k4P1Z zo5}xxXcXy$f)AF;Z)Iq%WkU8zl>zZbTk!hi#;>$DfP5BP>?5N9 zY{bN<2X1-ZvJJ2R0Dv0+2S5PKJOI?tppF0l1ORe>^IoF+N;m-UO#%$!xqoaZ`beA3 zI$CSe$_nFUiTr9qUr<^oD@AtPTbNy`#gdh0Rh)m;JLWD9P5bTDLVyggR8e5kIiE`% zwFPvnvXK<$gddcDOi?0+*I}AG$3kJ3lENRC-sKNdDr7o>g*Dg5>-*w`G8buOqh;tV zFS^9jq7^3r+hF4`YT{~Jn%}^p9kjx1zt!&t(;UW#u~$NUES zHfU$uF=E;O|zOlGyLZkaU^Ya-rQQh>*UCtbYT4ovPf z0*N920F(e*fEthnoB;qdXahrd0M-Bi9RORg(Jnzn006}<)ANU)%9!Ue@G7@)!?6wm z;LW|jsbeZC$y0C@7ektwTt&(--mfuIt*D;2>or=J_#0+=g+JU12YAT1>_1QQQFIf_ zvc$GowQx;?Mpxb$bG1M^b}&j5laMUo{w%>M0ebxO&T?GWVgWZ;5zT#;lm)BM>yYFY)=A({Ko z$-IgL0;4>6v~Sm)ex9S8@e7PS$gAs9aa>5V2`3~a{Hvs?;+RsDKVIe`q!LBq=9w@z zz^J1_fs)2gJ26c&9TA;SW`1ZB3>V6;(KBsX0Kmz+xRQAEW8@AKKgM}MeaROl#XGC4 zkC7-0IXrS82LJ%d05u>DI0FD^!UbffpNRm#004M9E%g@~M|kx6-GKlAz$%WkoR`J* zv9w_lsqO0E__rTF-?Do)P@v7{^JX&Xwitz4EpS!GCT_-XOg z$W^K8iTwztz*sGY?w$D0$FC6uPMmp-k_mF2C$QB5W46-J|ErKwqNJrkcPq$$qfW~mzgb2WzdN!WpGYvZ_uXv% z&VH!=489Hgc95rOtn)a38&X$SSZ(XredG5bv*yUw7i1P4>=i(#dx^;EZG2Bao=Szh z&Wyt5AtMEmPlOLZ>j(2j;PU3y6a$7p@aV>V8fR|Mo3o5I>ILBa??GM_fG~Oc(7ZZ z)ep=k(II&bYKHni6oEOsY{$aK`T%IS_NV4ryn=LP`GwT#D4%7W?V-u zHNz^M@)&iDB1sS^)<*U>M?7*8000002LQtT(m1>w00093&D(Kmn#2HrE|&SV9X zpFCVBoPN(;5;#jGYB8!O(X<849dOVMFYR%ors4gC!(wMnGg1@JWdqKPjzwicJz~V6 zeS7ROLLyhm;3aw1#2JaP8D~3Fxvn&aW_`6HQ>7+yPwatbFcaw@t0g$OQ0axXZZ^)N z+A~<|b}J)_#z>&Jpd`|OVi^pHi?!2b7Sd;KHuq~o$g5cz7(^L8k`!~(zd#7(9@G=A zxkN^;%+)E@PqPWJWSzrol1PQ|O%x+(%Zkm`O(NNL)9@`bEpp<==?`~ZkX>d~L|nBC zL4LR@`t0XRXy*T}ar)1N?(X>-{grr9P_mds5E3!1E%ME)_S)gF6rek~QTKtvR+cwO zt*xb?(s2CG_A6n7PVSy(TM<&{jZ*IS>>nf2bf+(&92bAX!aJv1na7xCq+QjHR|zxL zlwhZ7-ksAq){OJ4qwxvwbO^)^bph-3oI^8Zo+S8n^+Smb=K>Oif*5(i2@e!#v)`Jc z(I9yT{74@}zY;S4M5DCyIM!??o!Du4g6)3`Yc|>07*%YUoXU;YS9To0n3hj%bsEu@#PDo=-1`XdUqc<{^G4E5+MLU zAP%GfX8-^V?mN(gBBTId9oPUx5D)_Z001&`H#7hM4`qIp_e;Op6uUtUk*}k+@9NuA#ehxWg0~Xn(CxybvpL8g`DiHf3MI= zOYb^pP)p&^B^|vb3njsK;w=CGGXO<0fB=tYym!0*UY>jRUb(kiakdE@T{PXMTeg5{ zN*(BiHpU$Ul7bC^K$w`2gk_huFW_y!6!6jl;eHfEaUV&CI7|gl3gNwbAm* zNR1|>U$g)J)WU=!)%*epK&TC-z{z~1GT6{- z9Tn|a6wNqGXwk775XfPb)tuOhX*I$L)EMqJF_UbmZZ@>wtrnc2T(4@?%0?C+tVx|c zrvPWC!g6_=D*`Og^7b>6f$ic+PN`&dkyH+?rUK^JlXwZOMI8tM1i2CzXqS_Bl6ym@C_@!IMsHlOZO+s7C}0D!UxkgU zWv%D(opeb?KYnq4Lwzsi@ptBgDYgV))pFxEXm?vcCbhI=n@;7++48)JjIW^kcKdPu z>zG0MQ6MFj1xCSks2893tb)-?Yepo2vHdAbZJl**h}6Ezjh>WGYj3mW0e9%xw^^Lm z0DRz`I8HaGwb+?Y<4qoi?8PEpBi=0yu)J7Hg_6$UNwFltnBWxa{pmJ7pBnumVYHH* z8d9qGlq@T%tc>sMb4&4+5#xnCB=+v7t-d5R_mefD=@IuskRZ8BFUsXKYH_<6AAvqS zNd|WD*5sbo)KflPF=OhNE;j_HnLC|Rti8tFx_unrsycVc=V7(9Xgu5v65FW4z#c?jFuhR(+0@dn~|0{b`7ol04gBN)U?vx*0MV{zR=&@(a)sz1eK>H-~w?i5R38)38MHd+{5cV_Y+8^JO3jNW|_l9dxB(ht)01w?O6 zWF7`-k_f6AKFbE*J~z->gD5_Q<;nLHmaO2hZ-ybpA{tJa(~ z>um=ZYPbD7&yg`?%9?=t;?AV^uQ=ep%Ym}G+LwzxTgqz}4#W!#U0zn;wCgKsHdcadJ5}f_9rbmMNmUyH@a`pvsqI|K)XzwZ8=h`U9~cyQE;ZG{SlP2 zo)ws`O249|VWQM`Q(AEr1=Z~kQkDqgiWsy1Ye~Qjwwq6#aZKeel9h5b&?lp85`0~Q zKn=klH7_;QNd1cUwRrw*KD2jwzopVsijJp``J-*oW8flI-|*tE`%_QCCS2QV`>G!dXcwJYeKtU@S>oa^FJa5nW1EU}?JV;Rlvc&&GEZ|6KcN9}md4IcSDzL^ zB^+}L+Ntdgf9^diU-v6i&5+N}`+UC&gy9gj%GHb$y3}=W(HU!!TQDb|weM zNh1auH-wY100`qcSf92oOXl~qzQlPi3<2Pu$_I;+KJ|W<)qfu~K<(+Ny_b^#_exPa zo$q6O)s9phIXk-XDzK%-goU`I{vowA&igC}p>!AYpDqe%8E0{hj&a$2o_hJzlRdlf zWA`OA-?~LpN=a7L9P--BR-}TZcCd?67U{!t2YXHfvxufID*f(ki-MFrf1t0cr*|AUj(3`1nV9sTi9|GUBPV2hJTv%&~`1r;PY&d1(Z^3s<2hTlX zEpL!1W`AOwQF9Ww8Wo$P(w_d|1)hwAB*48Na*(Ro5m(@IN%p#LVc)`D7Rhz3p4hlV zCHR-8#a7^V0dw7OLLVwIiU`RIwNva)lUoGoUxol6U<`l;fC2~3>|byQhR`ryumb=9 zV+|%seHj42yEb}o!*5BTKU@NN4ffn3?$t0|;Og{_XaGHO`kK9KIK z`!a(A@Ge>D1=e%GVW!q&MLYuT>uH+SIpS#7$!TC>&S&7oB|6e~giWzAuhDIrc9i*MrHs261xbR<@8Y*_Vp9K z41{w`-zpEJK2A-4oAjFa-ZV5fJ>T9?ywJ3 z%QuG&H3N8K$$Mxu5Viqq0N*O5dRso5x`=B0`t?pC9x3uvz<{y0pi9G6%)7K;c@#V} zxr*Is!`5i^%)51q_&gCk`4g`3cj+wWKoAB117HCt0GRa#yWDHw06+i$?>j@fa4rS_ z2-kYd1+G$BxaCAI(mLO2cJxf&5lW&j59fT`Ryn=y8{-%ml9%_m3N7SbWq|D@5a6@j z>B(S)TZbl8(XeX591?~3#YVf9yJkG%B`kNtDPuA*Y<8KG>`BNUSp<)^xGWjUsxT|6 zl|6+HF!GD|UV8z*VA71>v}X2M%N?^>?L#QDSyGJ4V-Dx=(?+oCRPtG7|5EP^?~dRr zNkrP_AKj0;TnBVhX7yJ#yW|DS@>UBq?zP$7b2A8^Oijpy_Wco5 z1Yo0GyZq&z02XKcAR^1S-r=#%?#;q`jOZt5wfHf#+ zwDaA)&!oENUYe}%LE9P^ljdO1=3fJSWq&hY9LSW-yFTWPzaa!E#Va@63w{gw)*P~J$J?pI1ZqR`=As31dF zK7{aZSF03c@DzFObST+SG>XIJ%F~1AV-{^Ss{`#-_zT$^d2n004@4Z4Gu@kw)7;B=ORFfh+M_->?6sn@l`YL!~D^m^c(LoctPx1#ZC59Jc zIG7D?)}Dl96OO_$))Dt%*K*70R74HuCmAZ`sc9jV3L^ zLZ|@10|1x5WLC*lA1#E~t(^94C&r9% zd@a*ze>GFX;&4&EUA@GY5cQE@CROXoK5iw>NI2w)C3iB`DmgF?*>+leN5TuyER{n`B%DBT{S#ZUh$HC1j`uT z{SmiNHz&%KT0eYG4spR%t6=CwCRY1PcOU^ZQZxT-g-e=DKti9@Z4@Z~O?X=guUgfy z6z<{lC*f5t4C+f5t<_DxT_CFo^>I0WbiR0L=9OU}7MK006%N zm{j95VtD|IE&!y&FL_3-h84<^kx3FOL_LWU6~Ra8)Q=wfpZ-dbmqaSXD|6D#x9w0$ zyU9;oEZFtW%nBfV8ul_Ga_0VE`$2DN9h(!-Kl@9V6G#_;4ZsFK0)WndBLJxRK;!HH z008R;T%5SEa54aZ;a-oOaIQCCE6ul!HL`rpnQp3DdRp>_qRZAl?{p7ibf%kanUg)> zqJuGfXZpWX(#T9h#3WeVfR1*<=TLtY1G zzaf;6e>xd=mh8J%-}GCOBCd<;y6EM`VS>bC4lI+Q1qMqYrT(K9QqVih3`GS%1MmS* z0H8CVHGni)06+jc001%9mKkB)n&p-O0JPi!9Yu8Hnao*{6n=(Cee!{h+N?g!Q+I}V_rmzokU3T#%?XTX@LtmJ-2OQ%Nbuc zRiYJwP?`RXnjto$SFk5mC%rCQ7wPPJF2Y(vIbOnD9K+F)>UD(t7;YvwEI! zW9byWcRYoH3%|OL)f3hhhCGST)y0`EJ)Qce=#GKjKb%i1vKP7PC+?CKt`@_BxN_M? zwMAho6h>#|1%4t#^{&73ar1$_SumKwWPCVo{o8!*ZhWbl(T-jF`1rV7CsKX=opj>c zlRfh6MNsl{3U7=}@JLbJ9yPn7&$$VLc9Y%zJFZ=XNWT@4Ze%Qx$Sl;QOp4m?Cs`%! z?u3v(+w00HQf4)Qotk9nK-e(Ik5Z`wx(6SOkNt_@qv)2BgS_|!*6aJAbFsRa#KPso zeCe~`?wp%9GY&oK?cH2=o zaU@n-HD*(Qx3_P%+dI$Qw!5v{03>n0Vt9q|i{}vj0Eo;0(agY^fdP5#`sX0Ih;V0?XuHJV-Yintod?egj*0}h% zIM(L)eA1&>SzO+2NQ!}{n<_)V1Bd7kO9GGpfNvmT002Y+P$UL`_lMnkyYAP$yI%6U zcDu_g=WVZY({?wtPGxo5>O`3BxN#x~ikhl^V4xeBHEsZy2%!8RphArQ|3QEd5kC>1 zFrR;dpHfpzr<|^=Wu=R85~8fi=%LHEv{IW==bi2ZKyO8h%~N-ODhPR7h=>f-UR8FM z=ft#A-`4|Lk$V~f6hmEDrzqF1T!d)2k8+1@Ia%mesX4A9InAb_LejG+EXIXx%3(pd z+GmGn2^(0^b105$-rmB!_R}~E4uW*6Q)VJ%sdK5RnuJ=%f-OG{N$<_s-THVU-cH${ zLF){rM7Ic7l-dJn;>GhlhS(?q>f$2WS}V+1JT*dl4@l%wJ?6x-AJsUJ(6myR88eUD*O&qM|$yAy8`F+dB*Vpj<}r27J58Z z@wKx(f~JoCU*1E$phxoMgC3eC2=<-RcWmY$K<7Eh%Yr-&20eKW*20 z0{r*xJv}ZwntfR8IktH68 z7u{ryij!kYh}w^-`9(WFY;NzgVT69T+O1h%+Nsd&_)ej5+Mw7-(6|o53O7$*bo;gN z)@x_xBw|U-H^z8s-?|1ro6?Un?1|aPFn3W50e!f(M&RBvp8W2wG5Tgay7?l2)$}2n^yFKvOitrvpqrw-TDtwCuNkj z4X8Bw=@3x20>>QAV}f+q&|ghTl>-2eJyQ>6ZDzAl=|$9-+i$E@e`gqcPF9nVKPx|} z#fo)@8Uwyh3@1RG5e|$3g|7-L)RfA-i1sXL;Ygp~6>nE^ogi2>s97y@_%JKwC zGwml*DPf&nf|pA?CEXVd4q4Zsht!fvFN^1T!t8E)xdMH{NSd+_f1P3jip9q^Q1Z1X z@t#t>UqXpVQ|9agxYns=5OjFrNs-#&Nrvh}Y^Zf-5ntMq3pq|kGF7tZvhHnuFT27L zUO0G@yeEuB+4&%R^JlzEOE64ql8;+HOruaf!|^Snvf)<0?~-y3G0H3JXbqZ*I+}Ai znF*wPzD$PKraK89vt4|{0bfwBzf|2;=le=Mp-z^+A^Us~5Oil1T-(E~cnPd%^o}uEdnt5`B)3g5c3qi%r2EG;@M5>1n*7U# zAQ7jAs{gz1&xAmFru>YfGDf$ot1do8&(?hN-5L{xd0jtYq64(vJDzf?lzksGqvknw zd+3T$T%!UGZ#S(jd%q6fnQjoa!q{rx^e0_0BNVCi8^1!j-6}ef)l+*)nKMz$P|qLj z*@kjAJ8?Yr1a|R-0e!G~@R1n5jo4pRM$b4z)uuP_r$3(L4`wb;cBgA^hWe00;9`TB z{A2wnJ(3OsV%Qr$xGzkPLiyu@7dKPD&H^(eLWc_O!c^{hzYKE)S-399wxwy8m&rGs zLyBIbX1?1v`AMySc!;#VO$@11^#v12^gbp19v!1*=x~SIC~-INiYKB(epV!*(b3^d z{@~R3{L7v+ugE`nQCKchD^*EJxrW!}cD=^asz^cD0m_0e4{x*X5tY)5=48*KaV$bX z;`melWz)8Hmtxy1RkyZoe*O?Q%2xLE&uTC5A1Q8~v<&D+uII5rEoH>Xf_PMdto{`2 z{g7bHy?FC^9}$>~sRu)gKRdE2qlJjrN?G33z8iZL6YbZiCYaza2Qfq^I;Oz79pTXm zlqRhP2Kk$GyFi?ld8%M%8GkgQ^~5^UmQ|DMr?i0Z@mt;J?vvWGM;!0AO`JW9y9Z7_ z^rm_SU(S?x;A6M`3)1BNo(b{u05ehU0G12@13&<* z32RzFmgBI?00031u>U+V0076bk=qAL4+AV@-?h$li zk&jIYP&V2kWEf=~PPvDvS8#Tk z0!sbYbG87mz&yDF06-rALG}*I0Z{bsf0c~r9I6m*w0|D@!PJ-dtG}8gH+5UXg=9v3HD4_^?LGP9wTLmu zdsq9kI+~q`mz=09wds&tb+P*z>uh6?qP>e_WRJGY@Tw^CTm8J}a>KPwUlTXbUHx^davkNAE~I zTDcZMR3-+*%`g&8toe*J=42Z)G&lL-=`fKOW@+`a?ss7GZ=Xr)-izU4jn6N7v@j<- z&+VHrMx*D;_?WV0VIcffhGm$w4JP=@gDp8LD5m>C=ix0|4jqx~iIbqwdT=%31d8T6BpIol&W)>=ZxrY}K>t1$T8-byr3%|9RkBpLWdk z>gmO?)$bc%{le8VS!Z2O*3(fiW^%4#bE@f?Z1@n4w70W*h0{tJ)~!G=t&1DpVsN2i zpq~iGsI6+LdOD-7J+?+rdw7VRYN>sVgYBXvDrGU;AfwmYwKBH2$5y+%+W>dr z&p4n{II8q6V~3C0K4zYnmpZAd*pY3t=Gl%9gRWYC9)qIc7|`Lf5mCVq0g^Ns`PNYg zkFU-D7F@5w!^|5~&t$j-%{X zyY4Yw+mv!bohIyuSO?gs+%fs`_SJZATIpFkihSPDO9igN%gg=~wTyrz)N7=C1LK&m zojj6faB45R663fMA1*sN{es>yM8_6RE8&fdyXNuR5n%2qLqj?h+^VYnCm1Ffg+A?_ z;Hy4K%6{L9rsS*CGxCJg&f%(q*jkIcH`F8Z1#Q($BB8TtX|Is4nkjR0K6@6;b?D0WP z43+e9zPnG{hsobXytDE$?2YXcSP9i1S&z=0KL(DujmMeRp6B1chu}vKA0nWKiOkDq z-*C;3hvT03(|;CDu*8QG@7N>5b)u&FB0;$s`@N3HNN~r%5wO$2wFlu*M(&MY*J+MZ zIVJd4$CcmykE2l<6C+iUqh@FH=%zJC8VQEaKrrk>l8SJ9tyyJ*@toBDnJt~z`t&=dn9M3$( z&_fJ!W1}Mx9mB->_Fd^b-7vA-+!aO+m>4WGWhkM?IVRYh ziFt(n+6F+iufa0w4!o*?m-bIx@SQwbEUQt31!+x5_!l8p6 zp(f_;_r%g8I>s>h@TEx~3$w80QLTy`&MsaH3g&elrkqNe5-mCekE-6R&ut#?7h#8) zxfjQVRjGT)xtB+Kb0UyZVpA1J+3DEN`kXVL9r|o{y{Lr7k6Af$(3xku^X5NV^_&X( z_8tZ8qD{K^Pb+8XC+H75HFM^bbKrm7Uls-~7$Lsq&!7M6wP!xM<-eZk-EYBaLzi@V zPRspscle$<32~>zoDOkP!x_Oemh%Nq3HpD|rDgA^*>D-lJ~2ZV~H7 znm>0>&TT8(IN{FQZP2)N;u)N+*>!2lqs1fiE%$rBQ$^(bT(ydJ7vMzRxl0S zhOO<}U(teJwt~*N_IlL4{dD&P*_NGf*;`-B_szDxwWp_miyh!^w4d{*&P8Y2iNGN5 z5+SZ`+j{l*_MJZzb9;=nh`eVQWW4#F@rI&yLqB#u!`f{`-mCIj?Ho()dPALtbG8Gv z8q1+?!rBJ9A-jLu-;g?XCN&kE$+vU01Ge*Zs_5MEz>h{#CDxx0ZMU@YT2(tML&l7= zt-RnY5D)+W001!tU?czlUjXj?s=F*oBh!X=qtU`kn>8Bo(lDn&cU@7n%2hL>zi&eH zdP$oZGqVtS7;M#Vp$KCWeIq5*I+%>~WZyFeG%^51000hP_VxGl?!CP%t=-}LHJ?;3 z$J4B_n#G<(4=pYd)U#J-&M4{8vJM?mXhQ;Z?a3T1q4oD03TSP6S%MYXv>ACLOS)|) zrWg%qCQQ~gIcRnkN!X>GhzGLwrywb*(cj}Vx=$; zJ7MUFc_&T+ccSy$4xHnABJh8NI@@CS1EdUqF@R72U_Bq{Ty`cDelGW#7`|oIp$*^ z?)A~Z22dF~IVSE>-2D<|Nh=%q#-n$`;*Iy7&Mj(1MNQ*t--tcBQtVco*36F8P6ICh zGJ`i(fk*h5z8xA26n{i4>sOTOq*&AM)(^WUnu<@wRSaQ1pJE{R%=7aQ6xv<+^E-@J z-{uU1|4=c#<=y;MjP*2twE3}j#m;Uk?tboFsNz)-R3o+d-c3zZ#9sJvIPt+tK3Nb9 zQe3Xtc$GGvdyhN`VTw;RsYCXY%B>WG{nxE(mi$}Joc67J{2JjX?ej);vWnt(`Ev&T zB9)P?;y?UTOHXGWz1-^U6?2Jn^Z6l=^z)lwR}|ay1<(CN_t7Q!559A8SP=_jreDZg z{9@3?E%w+jxvbBMT+RJ)G5^TK{;c!o znLr0Pfj)}We?Y>Q>Vyv}L&ph6;v?{ZCwPbV8nF>QpASTBt%&+&U0ls)W{x8NvRD2b zw|$}%kGt1)6#nQ(AIF`a z#0k7r@J}OvLqlIfWH}U%ga7^C4c`q74Gj$qhJS@dbN@9oC;FfIb?V-Ge~Ieq-+N*~ z(tohD#H7;L(0}5NOf)-n45W1exbA%bUmw|Un)_oE`2%B-{*7*3p0?El-zNcWH*)sA zl1TK}mv57>MTetDq+*$&uqmM}B9CrX%l> z9ARykJu&P&+|<-*!`R7@#+#?2s|o&>#kN6Uo0;B;x+Uf$#|fpXKhn6r_? z!Q9ihsI!s7Cdy9Z35YB4>BUztW1bvRnY!>KU6Il8XH>4FXi3%s19d^<`PcQ`HpaF)pHW3_UJ=>k57 z8x*EMj+_Htz%OFe4{PZbf69gfV$2Jwi8=yJQcIw1tHY2oec}w zsAM{uF_K`F+1bY6k!q^58KY+E)Xp@9QZ7{Ni>}+fJ*?P3IuA!q9YG+)d4Ob+c z$~A1ZcGUn9oyM-*xNPNXVc?wi)_xu4ZW|j;G4M+UV$As0C#MRPIm+Yacoan z4tv8WN31mB4em%D1K%NV`$mByGzxL#)5xc>jw78$IgN50>@>(}k>g{hM^23#8ap_0 zY2?$`$B|AWoW?nhb{ga~$Z@gLBd11=jh!4hG;(R|GGM(zd(BFt{BZrV$D|x&@})deasK_x*tjxN!7nDRBgRxsKVqklh;>^p0RQ zJ3B&q3n0stv~S%3$lR#CI~RYRU_Nwb!vkf<7}+cu(nX%>7|$L^>mq@5Fo7^3+C|pw zhIP#ZUBvM21`sF(ya+R1*tyv0&5Pydp9Z}MuwFlS6^(E25k~aWP5Mz5}@5LdsLd)jwJl9?rb8N)&D!2NUSgG&gLY1@vtgE(1dV#iHhD4i8MHi^o17dtkJ)OHy=HjCJGU3WH&@^-H~jqAUi#t!E7;m%`6 zrm^+TvE$?TLuZg3o5z_u*c}aI&z;DQ2KM&OvZE7OVP|BAM)K>ZDQi&yhN*T+Mc5CwZ(}j*O(y)|W@d%0j%D>3E%VIdr?Tom9Af(b-#R zc@3$vTZw@ksnm_)!C>tnT@owt)xMuptEhIhIZA?yHcr*tFv2);BL~{ zT}jx^*4eL=++8}GmEL;YVB=qZA-YE0m}Q}v{$s(l)xny%68Ze|%scliKd~vpop=A{ zNhQA+Sz_Ei6$#M!QlAMt-w~|7`9HrY-(Cx^35g~~U~ckkXD}{_`!2`zSndOVdE4J; z$F=M`g#Yx;XlUqbPKfn)Lo0vGXR`rFo}ZNyw0%J>dRtnG3c;e4&gWC6fJJ3V1jE~_ zWZm+5rEa+9#tUg_CU~e_Sa~N_@qI2|uxyaJc($eTn6_5!-?>^0k-%lzy6A43J5y2+ zPSFLOwTCKboA|=@p5PCwZwqKrl;-;1Z1pC#I?%Ksstn5B2i2%+7v$Y>TepY)4J6+q zL}c2w3zLM~aQS9sI0#@}@JkSKE z(X_Rc2LaPzgNYbb5)=*NgruZ4B^cD*J`8c8K%0xC0-G;v1{!pPvRC1(CSUaF*!3{@ zxSjPH>2j^%)r6^6thn;nDv}GdQGRQfDobV9Yc2rw$gE-OX}xt0?B3QK>vyiIx3zF# za~-rMH3n|P!F6v~>q=8npoweUxuGi6RW*GqVm(5<*vSd?EFoA73D3@NGwhs0bgqGj zH%hpw$$|hzK)JubwWtU{S4wONS*nl03ZIT)1Hs6!T{ET4YU@P2fe2GFJ8Qhlf#p1F zvT6D6Ece~ZM=6WJez#Eja*q##fN<&y6nTPeJ$ms{f7wuQSuLg&=F67IX9%OyqiVZf1Wifsni!qZ3j=;E zDH5^ekrDExD}z`3CFArOv8|<wbsqht?2NfJ~}O4ChCd|aJbyg z52!Bl??T`LLjF#;6%jlf2p(_K;;#kMt3eC;+YMMKsJe?6BE)&c3Ar=x;TP^r1<0)* z3)n|BW~Cj%Vu4sIAfSNWu9sN`P@5}qZB)u=Q;@>(QYKS8Q>>u)63TuDzbkR&({bMY zD%3@ECeQNS&Q6mZjBXJ?@hqB_OAs`}>4L4+_tar7G2w{x=)c_&g`L+uAnA77l_g(u zLj}KQ*(-aSRYg_nUTRY*m7~RXtkP6dyE+9jtHRBpZ4j@At}UMJcOkQAL;k&VHXG7V zHF-46p=;O2H5Ec6Y*}ewFw|jHoirYqFbEl!hVacq^IDX5YQ+ro2gdu(AXMy48_T`@JA&7L*&e0ez^W zA+Oe^Bi1>*0?7@^BO3|_Qe;{Wgrz5^UaDW!ImMIjcC}wNbh!)wryUR;a;Xi zfV8H(1s#UEW!ck_{jZV7p1|nVmJkPx?1ye9bFb`42bA^eFkGRD-*on_&ArhC2bG_y z-_OoVgz0AaH#_0Y!ht`wAU4HKAtUD3SgDodcCa!%=|D}c9Jow{cOtbqeE#guo>2T~ z#$^Hk^47=Xi)i`SZh|fFBiL_^l@$ARg=n%Vb{l-ZLP1pl&}o^=e#4{8MD$xg6F@Z1 zu&a1bxL}KR3k+L1T4~|dW}u~mmJ^QyW%hIRZ!#-%p>E!&xHTM=TD!NiVpQcIC*Hv& zvu!=1coe3|2KVqHj2SVPaC}33e5S&Ox+jDIT^)mYg#oV%cM?27Bu7{lU@GiN?_xnd zvbL+u7k%_?L0Pf}ZCkCZsj=)kA)sk(vA4;fJZ>-b@VSgl2s$t@Xm8QL&tc-Ni)b)y zVBL=zXyLhiO|n`c454sA>F8?EN-_CGcPNEfCs0Lf%WKPMsfbTREU*==?lAZ+vv`l- zT2~N4c{SQ#g@JN0?5LU3VwDzWm?VB0Fg=lJbm91gHEBWFWg4a;kN@t;O zDKK@H?>KU$y5b=V?9+~QH5IoLLW01ru0S~YZi`G1>D}L1RskrmX5hjvJ&%UWmjX;zo)UFHM1+5Fs}b7r0%j-I>Ic_y+!E5Z6SH>K@U z5s_yi#_X74M{TuwcHPi7ad2ylceM>XN0<-p@a{7Qo2Ar<^!M603jmh~%b;}KyAqb) z9(DlSzlk@)utfk2TSW51rVbWsWkzAV1q!yH)UeX(z>!(lfA$&W@~oZozuk2t_ROz zgT-J7RfA&FV+93lcI_0D@&!YbqH>VrFyM*QPk-x3TZuNW-7O}mQUHu zu?$yQ`?uhr304^@owL1D?M+>1p`jN_+ij^#P6V{1LRK+=EvYp7X6HF_z$|cx5Ct&g5fFUQfriWt7Cso^#(a;dy#+nXkNc}U6N^-RqfU5_)&e-F3n!vT zTsFY;yAmN1t0Gkf3zGy!qvcj4>w*dd_}G*rF9}tVCCI=Umb*a;JQC~E8wmFJ*4sHcM)4j2Q`+NsP$FtclAdUUDAes3lb z(mLvSa=KcuBDkHzRd7)1Re9j-_ZSCeU23s5GyL30?||lAJ*&>uJ0rS+CFlmhv#$vR zD$0|(QqK+xnxBFRs-?Z&itMF^V!9fPr#_ZI&nUKAJ+cu{cv>gF4`TwZd5ceU377i2 z$fFRq5FkbjT5c3Q_8M6DQH97Gl;aQaRef(AOm2Q!x#|HI1L&vdEz>Eeh034?!tc;_ zh{2VxHO&x0{~og5<-`UOmPp*+9o6QEkay>1SfC{B4*`E_`+tC4g2MUegLV&_%RA}$ zoR$rgXDR{_BgnAaygduu+YhKfc^u@Lx)*G$TfxFqT`vK$rRe2#ghh}5w(0A>+^~J7 zJGFr-1uXY*Sb`O(>y_aRUHFxn`EJ$quY${%YV9`a}`pkO=TlrH{C{11JLI+A@Fz%ZkLgb~PYmy=px)3hp zgrb_~OHMR_s=~OFo@Km|M&_E3n#G5-;a>cB{)n z@7>dwdJJuxuh2)WZ|kYIK;ETW4KW8a8NYmWsvOcqf0PU66Z> zCZ@mg;!X%Cr4_vIybt(>A(kpWr=4%t&2)tiy5+WG2mt@RobR1o*N8c5|U zI14n(ofI0i8_zDzvi1V*EyDgNJir%xG-`A{1kp81?E;1gn}ml1)ilqIC>pABvYwr~ zKqwjcFxiVmJw_2r3KP{Ton8bBNw6e_N`pkB19d`$_44XBNi>KsppiL|P4b9_$Xgk@ zlBKY6?r;f+mWq`9z+{6Sw^GrJkkTsLC54KCczhW?jwfeW<1Pzchh_#yO&)`|M|9ok zHC-p#|L%t*Argcqgq&-K<=-gn?SM8apQ~j&LD6#D~ydkZVTLJfy&r$Lzd4 zGcme7;lSn6*nz>;QLa3GkPmxbGTX1jBenl2UsSCl)Uqe1}734LjB7(aG>WQ za~Af(&g^_y<`VB*+tbD|AHcyH_VsKt@1S%q!S$fvK6Dkn8?T?AMvZ1UZ~Q48BMW>* zotGDXJ1(22OkXDO~qyC!wS}GBjeohA@I({>RPpRz-U?0PCDd<+%kY^!v#P?hCBobew$Iu zH-l+bA|Mt8r!NQ3heN&RKrAJR(km8ELM;$vk6jL7iA)9IgAc-jxuc> zZVPk=p`|+w!i5#bw}rm^6=zrOkwxd;iAj_2^zQ^Qj#}_DS_rK45&|TOaUc3hv5=57j1swz?0DsP1j-kS{ZO*l0xL98 zrD2)LL1-27^1M3LJyF!3tD5CxM%w~(Q036US9HW8P$llfz@bfV^LvhHIt_6lWZxfN zY3=2RJgFA`!Pzen^(ca6v^ZKS0>~#~p&}IpWNuGYBzGr2RVcp_(@mf(TApYML|aPe zi%JE=7nxgPkp^Y$Zr!&M-uk@X{qaT9oe9O#qX9hreR2JXX}{{XRo~}6|3BVpT0fYb z@UTkAF)<7%k);Aet32W~I=4bpsbWNPyeM){Y8PGdKG5rSdzL0eZyx#SAtgU0Qn6G4 zmso-+l@?wMtyTAlOna_IN~)^kNXBbCW*&W4LkYJ+UY4bvGe*WDQXgypou^>nj>kMb z+=ze6er>UN<@ZXm!5bMv^1*s8pON=ZE2OlRqS=jayWGG>uwP$+<^FP}`%6e#tjA~l z0)X-C6SJ;m;3tX`1xWaYeX&nZK9N8SRGw*(x9E*d(r-xlyew&cL)K(;IV=%=qYO5> zTV}o`X*M2xY_8z(Ol1*7@El`HPp0Bz@AG9F7ntRmE!rj%dfU64oK1IoyTl_hGB-E1 zva~i@A@Svg1XiZsU5b3o6ekkB1%mO}n;e`DBz+@-f!rCJSxCUe#YCsGw0SXn7RuXV zCaROgzQoxIBxG)rV&DxD)xSI%5}TW9VjIbJaos)p(KC3U;%^}tyiVj6w+At9qZK33` z9pbxkfe?xk+mY}&=mVj=m#!HgB@{8%hGs@)#u?YuFPiTaH5SKWnYClmr=Wxy9D1bV zKNcQ?Z{b?^c34qI%~jh^|o+$sJQQ=vw$xYeO?Tlp;gi78wUB61&CGtQq?NCG1EZfj-=ZEgC ze0;+Q;SbFp`9la$O4su`Gy_||Z=R_9bTk{}5ZvM|*&fNiR;o1rNQ~T(W5>$ZsSeOZ zt@(>}xOCs6!uD78^8UL$*fplb4w)E}CH@m}>SAvs_qWL!K}|QP>}Oa|+z!NdjPf52 zwjnm!Qi*EulpBK(c&bLGMW=96#C?(zCyh+imWxl6NDQ|^ zq=(ODrDNmrW+M#uq9I&~t<1Nu+VPWatM}o3*2AZ8%6^wgTyKhR-i8fxxV!gPBF3T# zq>*x%s3KW>SWdWjxRADiBb#Lui1~8J1^rE5*2pBZ?UGBW^l>{)Ev#(u&7U@sbwZ}5 z6G7pb6HQQ=N~5I7^t&P|Bh4a_`G*P@`J&0D7Y5OiYABH6)aJ!ceTzyd7|O+xT1;J^ z`ACQ>L03{H&Sgj~k!fZ+KSyNYRX@+02vPcIk?=%}0Qx3mC~{+GXa1Lukn28<_}|^z zi#4FNxtYaC&UFWQ<^g`vT+~;yc3F7bytM~qLd6ii!3R2{a7`pgER_enXpw2Z=z7cE zh!XOC3W=UP?gX;pijYY0e;U*lE$_dqY5M%(Sc%0vXk4LFdku$90tsw=X0aI}<;2v8 zBzzV9OR(M}zb^uQ$=8%~g5Run3&}VLmIKXggww?4q?(Vv46RV*adsnRAzQxDp5`xZ zUqVN=N@vD}G9u$?xx=UKR-KLhf0AYF*;iVq!a0Scr)<(z0l14HvW?vx5}o_dY~jd= zO=M~FYGAJ!{O&5kAckr4JqAMAqP_z{It(WMO+J0W%f@39BIYQkwjmxtBl_}4!o=M^So|GTq+#2HWK{%KbPv2~G#FqJdrlH`i zOi5ogoclluoxdL<>Ftk9KUh`Y-f-n6+>_Nn-NsliF6ioHn8six_Zm*w9q2O;>Thl< zlkDNXcl{;MGvOUeB=IPb<|MxiR{zM{MJ#5T^k0LoOg+xB#mq@Ky=u9dzqDJL;vL_SmZQatds+eEJBBm&OuT|0Tb0BIg1-OVbH2m*-trEo7|!;mYPA>mAB2D|qxyB3WCKJN^Kg1*fBsOtIjhu+6)y3PA#uT6-g^voQ(k_YV{f|l?AFW$=5hjbd_CfRFgGzf_t@DN zk#Ckhjm47Tii>VOd!8n0j{!t&>VU%cgfosK=hKU&#i1a&vA-m8#O*eCsQPdI=fS%u zsWOEaBlS}&8D?NBhjO{M7LW-}CZYKPDVv!-+s%SI6>REa8OLaYp*6%CaVq~Q_0Kqi zrorHEsS$l)>DFRtPZoc~#oO_}Hf*r{A`x8>G?DDf{I3HMa zELa3&+q$iWS=S?$qsQHqmPc`m#l(a*r8EEM+$z>{D7bAt9CY5F^gYHjvI@wQi7>v& zuD&R3j$-IOOW)C>-cf|F>mebta;>#lRHX9)=IMVpB zNfMr>RrPI6D~+sEe7o@YURK{uK|OZDb9qJPo^m{OqiE-UN+{eS`rgnCz0*=Bb;+dI zO}Jb*wYQ0H?pU=6q#e=Q#Ng{RNg{4tZ&v9^D;Odaeotr^hGQ&I1kjEf$=P} zvuV;K+Unn&N=9N$DsKOdC?X+qKzaLfCo$tgxLH;1zqT@$6iXQ&th_1(CXY~GR#rK0 z)_wkwnTVSkBtEvhW~Vt>CZv7;qzCZo)Dz9r_uv@cg{KP@8H+AX_5Xw+1PF2bEwUB# zk#}n*E6=~+{~LR9sp3`Sk8#JJnFR?=6~oGj*>J9QxOu+mwp8NqV!YL5}hgqxDp zerQY=hsYtAix?7S5cL9zz#!M#`D2ULFX+mxIp-LcJA^e42SdnK+QBMtJFakgo7vBQ zLlEvmdrh&nov`X;ZwORadI1d^R03U*3)4hxOZabyb&y@QC6UV=7Hp8dnt4KFj}x;Y z%Qj$u3f|}@yU-g#5cbJXG;LWH0hkNRC!Yn!cPKDoGy{>m!bHD*5bJBXpCG{X+!~2> zaO9fh*OAM#M`pc3YL#4<9q9_SXWC3{u~a>q4WlDAzJ!m*D&$i-W(^rLEieVD;oCQ- zm3fgCg|00Dqp|{vNZEgBWa;=nJFNL#M4f*)>tkJSk)Q>`(<>yX!IsZxF_%c`i;>26?DWZ<71 zXfu?cwE1h%?aj8H;rn5ERbG$SMaV#XEz56 zwY^1OU;CVn_+}fZy=n)uvqSuCpaLtv*7L=#cH%~%!0p%+j$tVO~yr?U&-8vW@y|U?Ta_i-QzO1JW;xJOi5zn{^woyKbd( zY2hN0*=KPTpeG{}kTDBFLej8Pu-)j5Xlf5Is zUEexQp`^yk8Wpm)`oMnESc)AMlIdYdf4&nHBMNQWkM>HU0~{v zFHpteN-P$I7}zR8I2yJbEE%PMA9HPwMgs~2Jtm_r1jhcmGY(uG^vU)MsOtHt9LC5U zP+^^6b>BZf3zV{H10Yp)ik(9*o@6sXYwI%5<@&4O8(UUd3ngT0fTr6hU+Ly8A%Hpx zFC?!^-3eTsVbsUhTdH%16wiuCfV;bZ>E*9nRJSrB+_l=Ce}|i!1ObM~ zjy_vdxXTX%oC#Z+c zLr)rDn6Fxq6-i`v$aWsBO$75rN~#iolG-F9L>V|fiY~1+^pl2dBIK3vEfa@)D@3LN zqYQQyavM2nzd}X2JWU)GaZO1-R+I7YbkeP;Y_#f6-k(a}45@C{LR&h|o}&}BYhha_ z1={wui?3&%YV|I4i8~cTQtW$+{pS*K*Tk)c6wFe>AJ}0iEDmllM zUOfV!HhSE?r@&z&w+YSVa;x3mmxGk7SX1kZI!@jY?SSI3K=a%rK8c>Yr?vcfIuiQK9F;{erTeEnrN3)3?}?Y zOM6!pWaTz)x={sS*T;aC*%F}i@Kge24%|lqFW_HzoM%k&I^O`X3Y-sUX=q%ldNUF zt|**tztXQ=WXbyVjD`hlDW%IDTz^3*$AMSq?A5{`9m?!dw)`1=`kd16wd=DArYIhA zCm5=D-QLPsVYqgSo#)GDS(d|ya7iD| zPpiqf)`VC}KCAOsQqg{20-0f2c4fy+2h~+Ptx`S`It(g&oyv%NkD|YyHOz;CY(AJS zpa+M$nrZ5|wT@e4LU6NMTHsRmc(pelZu5ygT{Ltd>&Z!X!} z0L4M!gbB}9+p~hx4ku_-c!$pIoX>GfS?|1&54CM!^}jLC1RGJzVr^F0Xx4(8$q|%x z7kdYOHd5=ngvF86Ww|rWc`yI2R&3S%&AnrSR;`wr1`$GqFyHy2q}k&d-YKZl>z@0_ zNo)vS4`H=kx5esPtSkpW=NmjR9V&^RZN&}>)lkeX*PL<(`PN3T(VsEyjxjMS^N=*g?aNzSr*-?XBPM&2kD%A#8%QI}jj;0-D2-VVd z;;}xYM$4$o0uT0Ne*knH!v(Mn;=S%icL1qT)9e>Sf=pM*p@QQLqS@~PUh0iT6o zRAsL%-vaXnKC$nGpYIABniK8~KtOViJ7Hp4b#mh2`LOG2!GV?4g|356tFB~p&0SLq z0uCs!(g-@Oo?>=c-6q$KC>m@J*b{Mu{TD2|yCSvT=6?-AOmw2dr(4f(s)pIfmnzEd zn1=Dh!;RCUOyWY>L-5(5v7)#1<~B(j6Qd z$G(&T^^(y5BWCw=#Dyfv+rqQ24siz+4=al&g?-BXt0%yDVr2N9pbMg1UtydtW#_Ll zrh1hj5@B1Cj5igoYf>GH9akX&eL!+Tv=HuOvwqkVyZRD+KCVUgWmz6~zth_Tl;4Y-MQS@O(=S(C4P%%~GEim>Glq{)|~fNs$gX|iGh5gSM+TMYM?s!{O1MgHdV9|@&f!XKk7mM zvOaM~$Wr^#xwyP4Fiw}?(fceo{XM>7Hb7$T*{+#sox&U8h;?Yw3e{9@;O+W)puV1T z4$117VaO^w_Qx;$>+PobtqItT_P6Sts2kfEI?po$;M$dXGHqL&OE2TqA>Z2A9`;`Q z869{2*Dt`|rh(iVFtOjS7@I1`#?{S%L}hF3t0g3`k9bOW&bBOllq*k?=hwudz&!T z5&`yCpy?52vkKS1k@og%l>tgYaHAAdJL2%v;kbh3m&!7E5-`9}%U3HEO&Qdunr(R* z>cW6^z&8&U%|cRb?X%HN^sU|7F2z2}@`FrZ5ghAmGB2G`J{RxAU@Yh{BtZ0BnRo&q zN%P2S;xIqa@=s8pdypn3uvz}fL-sdI2I*`~i^l3Hy8 zwBTVXvu5{$f^%9^r%eVsQ(u5&E3x3DtIy-=h`qIjL!SqfLMQ@7?_i5NP%MUYkE+Ru z5T|&0qo6o;Y>;pr22{<{8{?FP331V|oBpL+$5fd;MSjJY=c~7ubf-KAHLP`V*xseu z^k>SNG*(Y^1jSuXabUZFRfD|k zR&xQjW^+7@lbHrZ2dU(}HdHGcT$>l{vElv23-3JEz{6?@0GVONvMeFx39GsKo zL87hcI$yTHp%`=n=>d;R|0s|V1RPsG54~@vUa@}WYJCuQka_ayCBCLtx$9kYFtXgo z0Xjo>ZpQ?7LZLnf-h~hEAzGR04Ivx#lGnD@0;q!0BPwdkSUZ8~q?Kr)A#o*K*`|w4 z9jvzl>$&Z}p7`n6HvD&?DvW$;H6C103Vl#IHUihR7MNI7B*~=>E$hHbR09D4iJ5o{ z9E9cJJ0nfXCD@r6YGI4h!j@}UrY%d|)uQ^L?NvMTpJX%PbnCMn;r7|Aql?95XgmlN zY{KRFw7-(2^nq8AEo9g;Wu*_6Pri^@Llp?x$`uqz!)&g4k8ko>Cr4Y(sSxPqSf#vxVL|so0wih7Y|Y?WKRiXPz(@q5ZR8Xy?sF}8v$V}3Ff_{HM9+}7|wpFGorEs*`5 zAGRP*+%@mz_AJFu$OU8nD|PJ^I@9*~A3<5|bzF0_jywphLY39i=8XzNjxtQ9ax_5p z6b87=qTt!@S@!PN8=)6O*rec}tC!h=bvX=D(Uv!c2pTOa!88Mc|Eq{JcWFIKx0#!E z$#vWn1O(!Yr|Sfy#gCVizEok82nw_2PD+wXut`3Pyd1gDbgPt2k&KY8G>|_9M9A7z*9uyimpzR)! zuf9Lj)6ks%4CXhVoqr?LmoxL?^CI#`VeE`a4^mw?eP2 zthb>76WCCRospU3Vq@*beTDXI^1{sK%-p_j)Ba*$Kbkvj{wyOeX`Od7LWUdW^JT`& zuap*8zCriNlXpekl!@=m0~1hJ(nhKDM6^elh#8DqG-~mDROlM92|4bwf8C}azKREL zXmW1uDP=&)%q3@ab-X_0XpmDdlkX?LHQq!Y zN^v~fip!3incDe!OOpAoW+~V)D4EhA8@^hh&f5{odyEhpi@Fr6#x!w1{v$y8Y2CiF z_Swf~O(5lTkDex@$L3Q6Aadea zd^LIK${KkVf;S%+6pvnyq+7aAWwD4^eQWB@CQMDsyBWn9m@-X`VNE?o#oBNuvu?i7 zO(f&{FyVwTylvAM^GgAW?X-5P1RkY=?4ku~ZOlB!TFyPC*fH~1H_qg@<5ZH$K-);4 zx$Pn>Q+kx3lyHW9CtXZ~uc7Ibw}KrMD3mX2=mjF=cW3ip%31VD%8v^~sWR#B7snZ_ zFll;nm}~zH_iq0(emV_7vxeDBmsT>egM zZIV!>oYygNcwsKK1xdKOoWjq~)-c^VuZ;k|safWJ)Ai&*R4KpZB5L73bRZ7{O+yvs z)b<$U_Oc-vd7)!KnUw#l+VVll-#I>L-$r_H9RHn-EJHlAj#4+9XI|42TA0;56y_pbSWHJIbxtZJ(j91D^ z@u+^wQGeF zGZ|qbI4Ax_#*@nn2^`~vCv4mDJ9%oi6rDsNnQWtDT(O&!^u~g3HVIWnZ{*mN5dD## zQG%w15qdqosSzXJ`tY%0Go6j-HyhKo>pY=OOpnaZhw2&!+8e!Qnl0 z<`TzU1V0b^b=}2@&p@I$&dL7?@LAoh_5sCjL5gz~$)4KMl7Gbr1vR6hKcXfL6F2QJ zzEe|=WPU>V*vcFIcb&_d`dfrc`7J+uDEow%#w;x~^;P4^r9S7A8-%Z7DJROw&>x~Q zCKtPkrxX1$JrItWreKZDl@p&gzYC$M2O$<>o~*z6n{4Jht^}r1bkGmpZD4KP&j~qX zy{h;)ac?3Le^mZS9sH3qNLM?ioB9MgHXA3r<0x;k&A%SXf-f<9Bz&K7+_v6tF0Lx; zD*9b=*396s6UsXl0X)9<8PfsyxC+{8am7C4`hF>?{VEA~hQIHDrK+7kdeo+n5>P!m zB@)G04@dA3DcaIXPoBJcO-%7IdYr7id5VkbF3}H%lCb*vT{|s9>#+i{XR-2PXx`#U zfW^pt&lOLLq{5MVY?z**^lvCWkW<-?<&twQrOoIpdCy1Kq57rAHs)aqfow%a6N3LPr6mygafx#qHkhx8(52F8CsoIieeg z=*-$rczoTyjj10+s6{?vVf25+w9jL5NpF?%+P^jF4LF>@PDuMW@vC=*E>pxH>dcBPfmK7fQyQb*`~sa{k&IO&v45yeu|`LWqZT(6R* zw6;r2UU7#Z#-RIv9;F1iX#U98Jqf?6!Hrw}WG5GL{I?#O7A=sGsUFD_54M?5M=5P14@yMK37(Ncqz& z_g@3#<49zYjYgklVjG_aJTtUS#(lGYFGzES`YFy;WcVk0WyCl>jmmd=9;V}S2;TD& zO}dYfSaNQlA{=@GIf+Q4dQ}+)KST_VA%N3o^dibE^HCCVi(!mJ-$!aG*7YR%i1M6n zn&iu8;(0EXuC*?=>X-me3d_`3{AdNu4AZ!VU)Wy6$g>xE?zG6A>F{1>s}C8F@y8Wh zPm4`@r6yl^gsROSq%S>AoO#qE6M#P3cv@uKcZPA^CffzN$1ODaGPf!7t!+4~Qu*}C zH~Civ{)FGNt~hR&WB<+P;Ge+0gS3^;L;dQ-k?NJJh~k%=?-&$25jUN{{Y90djo00@=1ww1Pd*^t_lKn3~)mJPB%=g61pyL{`kj|u1g z1oEI!^gC=&i9t2>K|ei+O#J;Z4fJ4Fw=N(|p*=P}lut!$bXDTiH4VtflP-HbxICT~ zv=?ZGZIZ^n4h90E(?t)GfzD1d9|}s&3X7K$mZR+sj!)5qP( zz0!TNzJ#Xj2OUAEl;E;5w@@H{CbfJ=?P~#mbCk@Gex9X#B%v+206M&Si2U@<-;t6uo#n?uh9#LGl19j68oP;9kp!=1MMJ!PkV#&! zB`-v;5V%PSnI5|iUUVgj8Aw$64zo;I&_S%)GIIbw}iax!C%wSPa3n)v{oI|3Q2&5&4`0qEueDC$^xV zRAUP&88kqZO?v9-83Y`5<|u812Dru6kH;ClENj~RK|`n`wuq~TA^H| znqx4nh_XnsFNGb_6ZTi#6dcV`wvt+iPB8SaCgpA|vJo;~g_T6p;6xmFrByOIGH@Bl z@uTj-Mhh`V!O54d#3!TwBt6fB?1VHc3zw-T$D-(18M4q$2;d4*+PUoY^h&ExZM{uH zHnC*~mC}T5RLE9~4Or)+7n^-Ul!PvM7B*TJKw6WWShw8jTR&!EJl-MRV5ea}h*kAaiq0#`H&t{I8x6USl0Une&ugw2($9=4|V{HV* z9z6w?-UXX;gyNmLSn?ao&9Ua60j#~X-k5D6JO5mlWT$NN}kB9N+b_tRuEY~F5NPt%@0|+ z0d*bXn$VDG!4)lsID$7zc?#a70#?rkEKrrFOu+(r2M$r%=hc9<+4UnKJDmx2k-G{ zD&Wp7tbjKnp+7 zDR>A1+&hUUo!9Rke(LKKDMUIKUAjTRREDlAzpS!>y^9DsaShN34ng1&y9%Qd0`0Xz z?!gk9i3st`Jn>U_pcms%`GV-O=b1fkB!P=`(s9W`oBC{M9S!X!!`n8%Nnyu6l zboJMi$#!s%OALB5$ZQlfgtg=Z=b8qh>qlCInCy9Xe0w90k3kOf(1gEa0IWQM~Q zz5%`pI&5T;3nf<~K4}9uN*-3mT?=&63w)d1_Q0FiB2NR{SUedrfp~rUBytCscR>Dm zBJnzqJkh-;HCY5Lr>PF)yYfXx0?|h&vQ&@E-7Kx8i@GOBT}oU%nMEA3cvAVRDU#sK zqO7?(Lo}&ZY$d}Ywmd>wy16EXGLR(SQ4msVC~HxnL)!v&#(-BBqD1 zhri4Nad|KHU{IAm(K8*DH2Qo(`I0vQmQ_27NC_b)T?Dyuj0VZ8o*y0jlUa39R8DxF zsjP8+fk5hO0a(As7W&MXL z99(!y0sgx*&|h*LGZhI0mf{Ae{Pm>8W`f2AK`6(?@a>;`PD>Gv*!o$8B6o&Xfw0ka z6T^Vqxi6^rMG)c=3|QoKyArbwDSgQbGkQv;rO#TRQa`P~kWyT-`ctXo)LV|)t0`?- z_l;;>KhD*Bt%bsVevX=q>6t-k^I2GW`bQ+H6}yCH2*IsGpx2oRR5rhC}j9mRu1 zMafaqPPAb|Yp7-3hAdas$I<~5SF+x?>B5wju6di5O^`+X8vOlUq4M5A+6Pqn1g8Dp zqQwwWb#!kh*3tp1*mVl)kyX9oO+Ni5wkEV+sWb07)|U=>&K`no~Z8E4~ zf$$tqI!Dq7W2&XB)`AnS;+jIe;G3S5Cxk6ST~7ZtU*(gS+n2+uGiH#(UAO<~i#ZmU2HaZ7HYZGp;= z4fWA34RX8@M%DR?YeuN_xt={gN2|0?%pMA}c=qcZSOi5EuFsC<)&?h=pEpadtMGAF zoBWx0xpgZ+<=YiR@CB=y_5~_JA5p1L(oe*B(wypR3N9DWq(H0S!?biE!t`9g;Ssv{ zFUBldfx`QY5Kw!$%cwSJ4l4(mb?~0j?_9J^tf@~+`->GpUP8So@}nxXbS^88u`{%g zC2C{F0XF}tDvMCD%Q|LkI6#XS+4rjK)tYkNn#pR*m{nc3H0D)#TTv>eOcBEC7@XrQ zttB|9eSvEZIaz^PO`v4QgbuHR$%9RgQ#!F1P;&#GTUqmMq_YC{Dh0I&g==R=Ka+h# zd4#X3QGe5*WLyFH3b$V;)oA5G$w*kUR!WwVdPLnQgAURpdMftin98>{;0y}M+9}%f zz|2XcR_oB?#ID(tetiee#&@1gQ5hn{A$XPweE5t@S40FY3e3QMTAvcW{y+f(IiP1umWBaypRa3 z2$x61isbQ0a03yPzE}@uBO)OObNM|^!My6eKa1EOss5oC+ ztQEP-su95mpazLQD@+3xS2Yvx1bCyOD$GG}6$J^mYp-ntYi4 zlTEGrNyJH3F9Q^m%uB5+btBB{Tal1lfgYJ~@TvBa3!rbWO$SwDYTY%hWR}?jgjQ=8 zy!G{e+wC5sZglA~p}^h=M4Ie)^g}=0+OVlp>({{Y)mwnifL1{7X!hq|r z85^qf-?ra9MCx>p4hui5k0jTo z_~d@X(`fgq>{1z1$=HhTaZezdn5QK9;9_FQD42_f zhltSjzL)rg{+PZMww<^Pycs%skrW_K9%qxA@1mqR^bcWaquFWcB|9Rpx@p84#` zyH4<~7mIxZtG*K01;q>W%EuR!5}@#!&d%W@~ zctMpc=rwJwXo@bg4$&^4Qi3!2Q5ik4$Dw?GVxs|6Aq`+T*mRQl3Qu;_rz=+yBbUo< z?ayG}gkhS#b?-r6mtbyS{rQ3+()IQ-I=F3e8)z<}H0%JJ2jy>A(v%Q|1!Br_H8(iB zC)7UH{|D>Dx1PWGlWZYOwJ6U8SoL8^xer_ z3H%TE1QXxfG`~%15c33MF->BiiShbBf(8cX2%Zm+L}C1*1S0r9Ra)zBG>!zw642bq z+d_>VDM<|27BSiXJOaUHX-YZA8+>LWQS`9{iQbtTW~15DPL z`WUt?^dccgSoz>lU7hv`%IA(!dl}c4KLKI*R6=OnrvV}l!{nK;xc`|vhkW9Z(zWW! zyOd0Z^bhIol;aWPsrpy-{P)Nz_*x=OO8L-vqKBt{z800y9BCO6X%zE+!f-;G?Rycg;CV+-ex?oV_3_FpReMV*~jJR1)8#?_`Otx>zwsM#MW zHBzdjC?yqCk*H0LO0C+7QnjUmqV`@ziKm>UCf{rF|A4m<#6?kR0g0TD{}8{ z`z6ub9BNL+8BN8q&MrNOlOzRnP)*8C+EuaA^&W`+x5$MfSs~UudS5BET%#sbUu=4> zjRx`z6FZ4R4(E&}8JBkntQZe1{tca7X_paO#*}qjO%ImUnYSl_ir*+$Qjy=lPXj@JP&&#K^3WVyTyO3Y!t*FqOE-`zL;zZf$N2zNqfo+P&6^ zabF&8#D0GME4-b_0Y#vu1a!gqeq29Q@9&IIYgBLR5G#c8cJ!ppQc`(J5mr2gBf9Eh zHt2&oi8ts<72xzfLVj$5 zlW)(Uz_2~z3NManR5a<`Lb%8HrWjsuET2T@kJZkxVQ`xf1y zFaqN(H;S2!o?Xkx9ri_&177&$(CcjpM9R+)CP{I&ZN$&-X%^Y=I|4ZVpUE9h-{=_L zH;rZDN-u*TSaHH_5En-+Ld^r5{sQ_>XsWt2?r^~ol(e+q*ZM@`f~ZWcD~iS{ zc|VG2u5a{z50tlID{sI^ZDp2Vuipg1z8p9>e^0uDaJpTDLa!HPWw2|gg>1wF)psb+ zRa+h0tsxi-rvqy`bMu#G!Ng`>J%4&C_T!VATN#~L$elVBt?5QuaNg@mqU;pId1dG^ zFC<9AI-{YOe}6`a+&0Wp5v# z=^+TmMdt-LC{?IfVI^`&qJCTxIf_S#*r7DM3x=o&KUkn1>XB)O zMz{nxlX!fh`KIsEjjKFIOKRSQo*uuM1AZr#kl05+>iEyOuCL2m=eQma3lX;XJQS_q z%)Fojxg=OH@>I>^NS&aSf+GEmZbiimRoHB@=GT04XnAi^^lUpPFk%gI)bGRlb1Ot} z-R^k_ZaHjM?jdmpFD2lOgt#1)BTik zdaNHPBlA_}<=%?W;4L|Ka-TqL`9~{2#Cc!zHRymJ#Y+RmEdE@vWN#`XWHvJY1Xu4PB#MI%Ys8QeesJ$*E|UoK|cprQw0pM zG3Jfi`;T;|{)&wXHip5C7yi`)9Tr&3ygFM=b>GGHNj746bBR-@KRi*=kO+TZ-a~jaPv@Pi z@Q?hNwl~%b;4PBE0RFQo7oBA(SbC|^xp{CwLzIRJlaT|_LVQg4%ArV6|-3qma!C6FrXZc}GlV!s9(CUkUefSE8K~0abn>Bho?sVqG?lNJ6!r zjT~aBv1)9wUn$$0xFxlUN?YuEk*<&IDMkjhjLf4H5mH*18N|Qp+LyuNl!^+pVV`gc z{DMOb+!u)Dk}*oSDm|S{&-de8qaon zC}KF^`+Vayc?!Pg5m^LfYVr0GO+((wyUsw9#kl%}B$K*C z_r=hb;(a^%e&~;@;QFr7z+7;=3jS@F;{m+-$3Tk;&`H6Cdk|QtP{vi<3+w&dUuk+R zFq;xLW-{@90WkZ@8f(m!Eox(UHU8a6CgF&~X?s5h=_8!#-Iu(pBAXs2XPDrBU6S3< zG*$)3q`(re(cc<&Z|rM&A8+gl2cryDNH22!Pu{Lon*xqj=(QO#Mp5zwQPNbbt_5HJ zbx#S;Sd%1AeGa<>VtY>Ix3fKf)bl!UDT<@ko+g7d02>RE-qU?e4V)4a8A8|c38RS% zoqhsWBUpMfPR`FdV2DV%Kk5Q&+;PXGCMA~29_E%m|K4Z)WJrV6_G#jBbijLw%D%U! z^MP@d6R7d>XxruV$@^oj({!KVCh=-p6enT6Ckkqd`H3TNHu&@y$v{MOEJ)ck?D7a?I#b`w^YCee~GCDRl?>P0(J zT^L9mOQ(Tl9+stW{jIUl%V$N*tSU#&t__;F%}Tp&JHanoOK(g}oN+%re-T{VrrfDc zOcysxbzL79{n@&+L`dM((r*UHRlW-nAV%}G4X7jhwM#qRL9R&bd@OF zyDM7Q>lEc6NUx;QVxnQ}vPS3h-06#;Tx|b7xS_^1?$09%-J*COimIw5K0KE796S#8 zSmW9@|ItE<1kJcMP+e@v=G?{l`gw69AKyG(Vy$mbW$aYLFj!@uha=ie)nE>&6FZHx zrNRtQ?TJejNKweH+}?NC$dL#1PJ#}6v<{QutpN;Fvfu)6{nna5(0;~m5O@6A2UWHi zQUPK+9`lk>QbkHEi-Lo6v_cezKFJ*0TZ@{j6!!lSs4Bd#;L*?}v#QC6r8Z-r?wmf{w|j~KU!42xeHKp_(bVBWpVr`V zT1b`Kko$*{_tw9Nb5^pe8Die%GmXKM&VR4heVRE2oXq~1ZUf18_bM)$3=rMZfmgOy zw`XhF!`My|5|-L7b7z0%Ck_a-^RLf(Y1$>&d*Af}L}4dS!7lif)`iN)(~aG|UsP4Q zKJ7>bTC?G^!v%;L0#~5l?BbGE@fSJa4E#NI1W7jd;24jdcgGgi-!(X=5Np}MY(~4Y zu+v%Xo3{f2x#@qE3oL-TQ2>3 zuaGl1#f#&bgHc2HNGrBg^{&6NN28`5e!7HC^a#q1OnSwe7C33n0Y3;Sdh) zCX}YRhrLBq-R6Q@Q9M_TYDIowX&}Z(21Km3h!E+Uh*?uq+O#b3SukPcNc-DjH_~XU zYwUsRn9Z4a*)vKNH|}!hd^}sE+2azV=2l!;5W-zvwkk7&LdrDmFP;bz< zy7QqRpQ#5Pca;1O}b_q&iqD)RJ53iK<*A~e@W+BR395-6y15k zc;Vx!5Zr<0vT=R%M+{&w9CxJ`R{|Y(!%J&#m&m_cS#B2ct$D761SM literal 0 HcmV?d00001 diff --git a/scintilla/deelx/doc/orig_src/deelx12.h b/scintilla/deelx/doc/orig_src/deelx12.h new file mode 100644 index 000000000..cb04b114e --- /dev/null +++ b/scintilla/deelx/doc/orig_src/deelx12.h @@ -0,0 +1,4665 @@ +// deelx.h +// +// DEELX Regular Expression Engine (v1.2) +// +// Copyright 2006 ~ 2012 (c) RegExLab.com +// All Rights Reserved. +// +// http://www.regexlab.com/deelx/ +// +// Author: Ê·ÊÙΰ (sswater shi) +// sswater@gmail.com +// +// $Revision: 864 $ +// + +#ifndef __DEELX_REGEXP__H__ +#define __DEELX_REGEXP__H__ + +#include +#include +#include +#include +#include + +extern "C" { + typedef int (*POSIX_FUNC)(int); + int isblank(int c); +} + +// +// Data Reference +// +template class CBufferRefT +{ +public: + CBufferRefT(const ELT * pcsz, int length); + CBufferRefT(const ELT * pcsz); + +public: + int nCompare (const ELT * pcsz) const; + int nCompareNoCase(const ELT * pcsz) const; + int Compare (const ELT * pcsz) const; + int CompareNoCase(const ELT * pcsz) const; + int Compare (const CBufferRefT &) const; + int CompareNoCase(const CBufferRefT &) const; + + ELT At (int nIndex, ELT def = 0) const; + ELT operator [] (int nIndex) const; + + const ELT * GetBuffer() const; + int GetSize() const; + +public: + virtual ~CBufferRefT(); + +// Content +protected: + ELT * m_pBuffer; + int m_nSize; +}; + +// +// Implemenation +// +template CBufferRefT :: CBufferRefT(const ELT * pcsz, int length) +{ + m_pBuffer = (ELT *)pcsz; + m_nSize = length; +} + +template CBufferRefT :: CBufferRefT(const ELT * pcsz) +{ + m_pBuffer = (ELT *)pcsz; + m_nSize = 0; + + if(pcsz != 0) while(m_pBuffer[m_nSize] != 0) m_nSize ++; +} + +template int CBufferRefT :: nCompare(const ELT * pcsz) const +{ + for(int i=0; i int CBufferRefT :: nCompareNoCase(const ELT * pcsz) const +{ + for(int i=0; i inline int CBufferRefT :: Compare(const ELT * pcsz) const +{ + return nCompare(pcsz) ? 1 : (int)pcsz[m_nSize]; +} + +template inline int CBufferRefT :: CompareNoCase(const ELT * pcsz) const +{ + return nCompareNoCase(pcsz) ? 1 : (int)pcsz[m_nSize]; +} + +template inline int CBufferRefT :: Compare(const CBufferRefT & cref) const +{ + return m_nSize == cref.m_nSize ? nCompare(cref.GetBuffer()) : 1; +} + +template inline int CBufferRefT :: CompareNoCase(const CBufferRefT & cref) const +{ + return m_nSize == cref.m_nSize ? nCompareNoCase(cref.GetBuffer()) : 1; +} + +template inline ELT CBufferRefT :: At(int nIndex, ELT def) const +{ + return nIndex >= m_nSize ? def : m_pBuffer[nIndex]; +} + +template inline ELT CBufferRefT :: operator [] (int nIndex) const +{ + return nIndex >= m_nSize ? 0 : m_pBuffer[nIndex]; +} + +template const ELT * CBufferRefT :: GetBuffer() const +{ + static const ELT _def[] = {0}; return m_pBuffer ? m_pBuffer : _def; +} + +template inline int CBufferRefT :: GetSize() const +{ + return m_nSize; +} + +template CBufferRefT :: ~CBufferRefT() +{ +} + +// +// Data Buffer +// +template class CBufferT : public CBufferRefT +{ +public: + CBufferT(const ELT * pcsz, int length); + CBufferT(const ELT * pcsz); + CBufferT(); + +public: + ELT & operator [] (int nIndex); + const ELT & operator [] (int nIndex) const; + void Append(const ELT * pcsz, int length, int eol = 0); + void Append(ELT el, int eol = 0); + +public: + void Push(ELT el); + void Push(const CBufferRefT & buf); + int Pop (ELT & el); + int Pop (CBufferT & buf); + int Peek(ELT & el) const; + +public: + const ELT * GetBuffer() const; + ELT * GetBuffer(); + ELT * Detach(); + void Release(); + void Prepare(int index, int fill = 0); + void Restore(int size); + + ELT * PrepareInsert(int nPos, int nSize) + { + int nOldSize = CBufferRefT::m_nSize; + Restore(nPos > CBufferRefT::m_nSize ? nPos : CBufferRefT::m_nSize + nSize); + + if( nPos < nOldSize ) + { + ELT * from = CBufferRefT::m_pBuffer + nPos, * to = CBufferRefT::m_pBuffer + nPos + nSize; + memmove(to, from, sizeof(ELT) * (nOldSize - nPos)); + } + + return CBufferRefT::m_pBuffer + nPos; + } + + void Insert(int nIndex, const ELT & rT) + { + Insert(nIndex, &rT, 1); + } + + void Insert(int nIndex, const ELT * pT, int nSize) + { + memcpy(PrepareInsert(nIndex, nSize), pT, sizeof(ELT) * nSize); + } + + void Remove(int nIndex) + { + Remove(nIndex, 1); + } + + void Remove(int nIndex, int nSize) + { + if( nIndex < CBufferRefT :: m_nSize ) + { + if( nIndex + nSize >= CBufferRefT :: m_nSize ) + { + Restore(nIndex); + } + else + { + memmove(CBufferRefT :: m_pBuffer + nIndex, CBufferRefT :: m_pBuffer + nIndex + nSize, sizeof(ELT) * (CBufferRefT :: m_nSize - nIndex - nSize)); + Restore(CBufferRefT :: m_nSize - nSize); + } + } + } + + void SetMaxLength(int nSize) + { + if( nSize > m_nMaxLength ) + { + if( m_nMaxLength < 8 ) + m_nMaxLength = 8; + + if( nSize > m_nMaxLength ) + m_nMaxLength *= 2; + + if( nSize > m_nMaxLength ) + { + m_nMaxLength = nSize + 11; + m_nMaxLength -= m_nMaxLength & 0x07; + } + + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT :: m_pBuffer, sizeof(ELT) * m_nMaxLength); + } + } + +public: + virtual ~CBufferT(); + +// Content +protected: + int m_nMaxLength; +}; + +// +// Implemenation +// +template CBufferT :: CBufferT(const ELT * pcsz, int length) : CBufferRefT (0, length) +{ + m_nMaxLength = CBufferRefT :: m_nSize + 1; + + CBufferRefT :: m_pBuffer = (ELT *) malloc(sizeof(ELT) * m_nMaxLength); + memcpy(CBufferRefT::m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT :: m_nSize); + CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize] = 0; +} + +template CBufferT :: CBufferT(const ELT * pcsz) : CBufferRefT (pcsz) +{ + m_nMaxLength = CBufferRefT :: m_nSize + 1; + + CBufferRefT :: m_pBuffer = (ELT *) malloc(sizeof(ELT) * m_nMaxLength); + memcpy(CBufferRefT::m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT :: m_nSize); + CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize] = 0; +} + +template CBufferT :: CBufferT() : CBufferRefT (0, 0) +{ + m_nMaxLength = 0; + CBufferRefT::m_pBuffer = 0; +} + +template inline ELT & CBufferT :: operator [] (int nIndex) +{ + return CBufferRefT::m_pBuffer[nIndex]; +} + +template inline const ELT & CBufferT :: operator [] (int nIndex) const +{ + return CBufferRefT::m_pBuffer[nIndex]; +} + +template void CBufferT :: Append(const ELT * pcsz, int length, int eol) +{ + int nNewLength = m_nMaxLength; + + // Check length + if(nNewLength < 8) + nNewLength = 8; + + if(CBufferRefT :: m_nSize + length + eol > nNewLength) + nNewLength *= 2; + + if(CBufferRefT :: m_nSize + length + eol > nNewLength) + { + nNewLength = CBufferRefT :: m_nSize + length + eol + 11; + nNewLength -= nNewLength % 8; + } + + // Realloc + if(nNewLength > m_nMaxLength) + { + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // Append + memcpy(CBufferRefT::m_pBuffer + CBufferRefT :: m_nSize, pcsz, sizeof(ELT) * length); + CBufferRefT :: m_nSize += length; + + if(eol > 0) CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize] = 0; +} + +template inline void CBufferT :: Append(ELT el, int eol) +{ + Append(&el, 1, eol); +} + +template void CBufferT :: Push(ELT el) +{ + // Realloc + if(CBufferRefT :: m_nSize >= m_nMaxLength) + { + int nNewLength = m_nMaxLength * 2; + if( nNewLength < 8 ) nNewLength = 8; + + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // Append + CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize++] = el; +} + +template void CBufferT :: Push(const CBufferRefT & buf) +{ + for(int i=0; i inline int CBufferT :: Pop(ELT & el) +{ + if(CBufferRefT :: m_nSize > 0) + { + el = CBufferRefT::m_pBuffer[--CBufferRefT :: m_nSize]; + return 1; + } + else + { + return 0; + } +} + +template int CBufferT :: Pop (CBufferT & buf) +{ + int size, res = 1; + res = res && Pop(*(ELT*)&size); + buf.Restore(size); + + for(int i=size-1; i>=0; i--) + { + res = res && Pop(buf[i]); + } + + return res; +} + +template inline int CBufferT :: Peek(ELT & el) const +{ + if(CBufferRefT :: m_nSize > 0) + { + el = CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize - 1]; + return 1; + } + else + { + return 0; + } +} + +template const ELT * CBufferT :: GetBuffer() const +{ + static const ELT _def[] = {0}; return CBufferRefT::m_pBuffer ? CBufferRefT::m_pBuffer : _def; +} + +template ELT * CBufferT :: GetBuffer() +{ + static const ELT _def[] = {0}; return CBufferRefT::m_pBuffer ? CBufferRefT::m_pBuffer : (ELT *)_def; +} + +template ELT * CBufferT :: Detach() +{ + ELT * pBuffer = CBufferRefT::m_pBuffer; + + CBufferRefT :: m_pBuffer = 0; + CBufferRefT :: m_nSize = m_nMaxLength = 0; + + return pBuffer; +} + +template void CBufferT :: Release() +{ + ELT * pBuffer = Detach(); + + if(pBuffer != 0) free(pBuffer); +} + +template void CBufferT :: Prepare(int index, int fill) +{ + int nNewSize = index + 1; + + // Realloc + if(nNewSize > m_nMaxLength) + { + int nNewLength = m_nMaxLength; + + if( nNewLength < 8 ) + nNewLength = 8; + + if( nNewSize > nNewLength ) + nNewLength *= 2; + + if( nNewSize > nNewLength ) + { + nNewLength = nNewSize + 11; + nNewLength -= nNewLength % 8; + } + + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // size + if( CBufferRefT :: m_nSize < nNewSize ) + { + memset(CBufferRefT::m_pBuffer + CBufferRefT :: m_nSize, fill, sizeof(ELT) * (nNewSize - CBufferRefT :: m_nSize)); + CBufferRefT :: m_nSize = nNewSize; + } +} + +template inline void CBufferT :: Restore(int size) +{ + SetMaxLength(size); + CBufferRefT :: m_nSize = size; +} + +template CBufferT :: ~CBufferT() +{ + if(CBufferRefT::m_pBuffer != 0) free(CBufferRefT::m_pBuffer); +} + +template class CSortedBufferT : public CBufferT +{ +public: + CSortedBufferT(int reverse = 0); + CSortedBufferT(int(*)(const void *, const void *)); + +public: + void Add(const T & rT); + void Add(const T * pT, int nSize); + int Remove(const T & rT); + void RemoveAll(); + + void SortFreeze() { m_bSortFreezed = 1; } + void SortUnFreeze(); + +public: + int Find(const T & rT, int(* compare)(const void *, const void *) = 0) { return FindAs(*(T*)&rT, compare); } + int FindAs(const T & rT, int(*)(const void *, const void *) = 0); + int GetSize() const { return CBufferRefT::m_nSize; } + T & operator [] (int nIndex) { return CBufferT :: operator [] (nIndex); } + +protected: + int (* m_fncompare)(const void *, const void *); + static int compareT(const void *, const void *); + static int compareReverseT(const void *, const void *); + + int m_bSortFreezed; +}; + +template CSortedBufferT :: CSortedBufferT(int reverse) +{ + m_fncompare = reverse ? compareReverseT : compareT; + m_bSortFreezed = 0; +} + +template CSortedBufferT :: CSortedBufferT(int (* compare)(const void *, const void *)) +{ + m_fncompare = compare; + m_bSortFreezed = 0; +} + +template void CSortedBufferT :: Add(const T & rT) +{ + if(m_bSortFreezed != 0) + { + Append(rT); + return; + } + + int a = 0, b = CBufferRefT::m_nSize - 1, c = CBufferRefT::m_nSize / 2; + + while(a <= b) + { + int r = m_fncompare(&rT, &CBufferRefT::m_pBuffer[c]); + + if ( r < 0 ) b = c - 1; + else if( r > 0 ) a = c + 1; + else break; + + c = (a + b + 1) / 2; + } + + Insert(c, rT); +} + +template void CSortedBufferT :: Add(const T * pT, int nSize) +{ + Append(pT, nSize); + + if(m_bSortFreezed == 0) + { + qsort(CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), m_fncompare); + } +} + +template int CSortedBufferT :: FindAs(const T & rT, int(* compare)(const void *, const void *)) +{ + const T * pT = (const T *)bsearch(&rT, CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), compare == 0 ? m_fncompare : compare); + + if( pT != NULL ) + return pT - CBufferRefT::m_pBuffer; + else + return -1; +} + +template int CSortedBufferT :: Remove(const T & rT) +{ + int pos = Find(rT); + if( pos >= 0 ) CBufferT :: Remove(pos); + return pos; +} + +template inline void CSortedBufferT :: RemoveAll() +{ + CBufferT::Restore(0); +} + +template void CSortedBufferT :: SortUnFreeze() +{ + if(m_bSortFreezed != 0) + { + m_bSortFreezed = 0; + qsort(CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), m_fncompare); + } +} + +template int CSortedBufferT :: compareT(const void * elem1, const void * elem2) +{ + if( *(const T *)elem1 == *(const T *)elem2 ) + return 0; + else if( *(const T *)elem1 < *(const T *)elem2 ) + return -1; + else + return 1; +} + +template int CSortedBufferT :: compareReverseT(const void * elem1, const void * elem2) +{ + if( *(const T *)elem1 == *(const T *)elem2 ) + return 0; + else if( *(const T *)elem1 > *(const T *)elem2 ) + return -1; + else + return 1; +} + +// +// Context +// +class CContext +{ +public: + CBufferT m_stack; + CBufferT m_capturestack, m_captureindex; + +public: + int m_nCurrentPos; + int m_nBeginPos; + int m_nLastBeginPos; + int m_nParenZindex; + int m_nCursiveLimit; + + void * m_pMatchString; + int m_pMatchStringLength; +}; + +class CContextShot +{ +public: + CContextShot(CContext * pContext) + { + m_nCurrentPos = pContext->m_nCurrentPos; + nsize = pContext->m_stack.GetSize(); + ncsize = pContext->m_capturestack.GetSize(); + } + + void Restore(CContext * pContext) + { + pContext->m_stack.Restore(nsize); + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = m_nCurrentPos; + } + +public: + int m_nCurrentPos; + int nsize ; + int ncsize; +}; + +// +// Interface +// +class ElxInterface +{ +public: + virtual int Match (CContext * pContext) const = 0; + virtual int MatchNext(CContext * pContext) const = 0; + +public: + virtual ~ElxInterface() {}; +}; + +// +// Alternative +// +template class CAlternativeElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CAlternativeElxT(); + +public: + CBufferT m_elxlist; +}; + +typedef CAlternativeElxT <0> CAlternativeElx; + +// +// Assert +// +template class CAssertElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CAssertElxT(ElxInterface * pelx, int byes = 1); + +public: + ElxInterface * m_pelx; + int m_byes; +}; + +typedef CAssertElxT <0> CAssertElx; + +// +// Back reference elx +// +template class CBackrefElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBackrefElxT(int nnumber, int brightleft, int bignorecase); + +public: + int m_nnumber; + int m_brightleft; + int m_bignorecase; + + CBufferT m_szNamed; +}; + +// +// Implementation +// +template CBackrefElxT :: CBackrefElxT(int nnumber, int brightleft, int bignorecase) +{ + m_nnumber = nnumber; + m_brightleft = brightleft; + m_bignorecase = bignorecase; +} + +template int CBackrefElxT :: Match(CContext * pContext) const +{ + // check number, for named + if( m_nnumber < 0 || m_nnumber >= pContext->m_captureindex.GetSize() ) return 0; + + int index = pContext->m_captureindex[m_nnumber]; + if( index < 0 ) return 0; + + // check enclosed + int pos1 = pContext->m_capturestack[index + 1]; + int pos2 = pContext->m_capturestack[index + 2]; + + if( pos2 < 0 ) pos2 = pContext->m_nCurrentPos; + + // info + int lpos = pos1 < pos2 ? pos1 : pos2; + int rpos = pos1 < pos2 ? pos2 : pos1; + int slen = rpos - lpos; + + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + int npos = pContext->m_nCurrentPos; + int tlen = pContext->m_pMatchStringLength; + + // compare + int bsucc; + CBufferRefT refstr(pcsz + lpos, slen); + + if( m_brightleft ) + { + if(npos < slen) + return 0; + + if(m_bignorecase) + bsucc = ! refstr.nCompareNoCase(pcsz + (npos - slen)); + else + bsucc = ! refstr.nCompare (pcsz + (npos - slen)); + + if( bsucc ) + { + pContext->m_stack.Push(npos); + pContext->m_nCurrentPos -= slen; + } + } + else + { + if(npos + slen > tlen) + return 0; + + if(m_bignorecase) + bsucc = ! refstr.nCompareNoCase(pcsz + npos); + else + bsucc = ! refstr.nCompare (pcsz + npos); + + if( bsucc ) + { + pContext->m_stack.Push(npos); + pContext->m_nCurrentPos += slen; + } + } + + return bsucc; +} + +template int CBackrefElxT :: MatchNext(CContext * pContext) const +{ + int npos = 0; + + pContext->m_stack.Pop(npos); + pContext->m_nCurrentPos = npos; + + return 0; +} + +// RCHART +#ifndef RCHART + #define RCHART(ch) ((CHART)ch) +#endif + +// BOUNDARY_TYPE +enum BOUNDARY_TYPE +{ + BOUNDARY_FILE_BEGIN, // begin of whole text + BOUNDARY_FILE_END , // end of whole text + BOUNDARY_FILE_END_N, // end of whole text, or before newline at the end + BOUNDARY_LINE_BEGIN, // begin of line + BOUNDARY_LINE_END , // end of line + BOUNDARY_WORD_BEGIN, // begin of word + BOUNDARY_WORD_END , // end of word + BOUNDARY_WORD_EDGE +}; + +// +// Boundary Elx +// +template class CBoundaryElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBoundaryElxT(int ntype, int byes = 1); + +protected: + static int IsWordChar(CHART ch); + +public: + int m_ntype; + int m_byes; +}; + +// +// Implementation +// +template CBoundaryElxT :: CBoundaryElxT(int ntype, int byes) +{ + m_ntype = ntype; + m_byes = byes; +} + +template int CBoundaryElxT :: Match(CContext * pContext) const +{ + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + int npos = pContext->m_nCurrentPos; + int tlen = pContext->m_pMatchStringLength; + + CHART chL = npos > 0 ? pcsz[npos - 1] : 0; + CHART chR = npos < tlen ? pcsz[npos ] : 0; + + int bsucc = 0; + + switch(m_ntype) + { + case BOUNDARY_FILE_BEGIN: + bsucc = (npos <= 0); + break; + + case BOUNDARY_FILE_END: + bsucc = (npos >= tlen); + break; + + case BOUNDARY_FILE_END_N: + bsucc = (npos >= tlen) || (pcsz[tlen-1] == RCHART('\n') && (npos == tlen-1 || (pcsz[tlen-2] == RCHART('\r') && npos == tlen-2))); + break; + + case BOUNDARY_LINE_BEGIN: + bsucc = (npos <= 0 ) || (chL == RCHART('\n')) || ((chL == RCHART('\r')) && (chR != RCHART('\n'))); + break; + + case BOUNDARY_LINE_END: + bsucc = (npos >= tlen) || (chR == RCHART('\r')) || ((chR == RCHART('\n')) && (chL != RCHART('\r'))); + break; + + case BOUNDARY_WORD_BEGIN: + bsucc = ! IsWordChar(chL) && IsWordChar(chR); + break; + + case BOUNDARY_WORD_END: + bsucc = IsWordChar(chL) && ! IsWordChar(chR); + break; + + case BOUNDARY_WORD_EDGE: + bsucc = IsWordChar(chL) ? ! IsWordChar(chR) : IsWordChar(chR); + break; + } + + return m_byes ? bsucc : ! bsucc; +} + +template int CBoundaryElxT :: MatchNext(CContext *) const +{ + return 0; +} + +template inline int CBoundaryElxT :: IsWordChar(CHART ch) +{ + return (ch >= RCHART('A') && ch <= RCHART('Z')) || (ch >= RCHART('a') && ch <= RCHART('z')) || (ch >= RCHART('0') && ch <= RCHART('9')) || (ch == RCHART('_')); +} + +// +// Bracket +// +template class CBracketElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBracketElxT(int nnumber, int bright); + static int CheckCaptureIndex(int & index, CContext * pContext, int number); + +public: + int m_nnumber; + int m_bright; + + CBufferT m_szNamed; +}; + +template CBracketElxT :: CBracketElxT(int nnumber, int bright) +{ + m_nnumber = nnumber; + m_bright = bright; +} + +template inline int CBracketElxT :: CheckCaptureIndex(int & index, CContext * pContext, int number) +{ + if( index >= pContext->m_capturestack.GetSize() ) + index = pContext->m_capturestack.GetSize() - 4; + + while(index >= 0) + { + if(pContext->m_capturestack[index] == number) + { + return 1; + } + + index -= 4; + } + + + return 0; +} + +// +// capturestack[index+0] => Group number +// capturestack[index+1] => Capture start pos +// capturestack[index+2] => Capture end pos +// capturestack[index+3] => Capture enclose z-index, zindex<0 means inner group with same name +// +template int CBracketElxT :: Match(CContext * pContext) const +{ + // check, for named + if(m_nnumber < 0) return 0; + + if( ! m_bright ) + { + pContext->m_captureindex.Prepare(m_nnumber, -1); + int index = pContext->m_captureindex[m_nnumber]; + + // check + if(CheckCaptureIndex(index, pContext, m_nnumber) && pContext->m_capturestack[index+2] < 0) + { + pContext->m_capturestack[index+3] --; + return 1; + } + + // save + pContext->m_captureindex[m_nnumber] = pContext->m_capturestack.GetSize(); + + pContext->m_capturestack.Push(m_nnumber); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push( 0); // z-index + } + else + { + // check + int index = pContext->m_captureindex[m_nnumber]; + + if(CheckCaptureIndex(index, pContext, m_nnumber)) + { + if(pContext->m_capturestack[index + 3] < 0) // check inner group with same name + { + pContext->m_capturestack[index + 3] ++; + return 1; + } + + // save + pContext->m_capturestack[index + 2] = pContext->m_nCurrentPos; + pContext->m_capturestack[index + 3] = pContext->m_nParenZindex ++; + } + } + + return 1; +} + +template int CBracketElxT :: MatchNext(CContext * pContext) const +{ + int index = pContext->m_captureindex[m_nnumber]; + if( ! CheckCaptureIndex(index, pContext, m_nnumber) ) + { + return 0; + } + + if( ! m_bright ) + { + if(pContext->m_capturestack[index + 3] < 0) + { + pContext->m_capturestack[index + 3] ++; + return 0; + } + + pContext->m_capturestack.Restore(pContext->m_capturestack.GetSize() - 4); + + // to find + CheckCaptureIndex(index, pContext, m_nnumber); + + // new index + pContext->m_captureindex[m_nnumber] = index; + } + else + { + if( pContext->m_capturestack[index + 2] >= 0 ) + { + pContext->m_capturestack[index + 2] = -1; + pContext->m_capturestack[index + 3] = 0; + } + else + { + pContext->m_capturestack[index + 3] --; + } + } + + return 0; +} + +// +// Deletage +// +template class CDelegateElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CDelegateElxT(int ndata = 0); + +public: + ElxInterface * m_pelx; + int m_ndata; // +0 : recursive to + // -3 : named recursive + + CBufferT m_szNamed; +}; + +template CDelegateElxT :: CDelegateElxT(int ndata) +{ + m_pelx = 0; + m_ndata = ndata; +} + +template int CDelegateElxT :: Match(CContext * pContext) const +{ + if(m_pelx != 0) + { + if(pContext->m_nCursiveLimit > 0) + { + pContext->m_nCursiveLimit --; + int result = m_pelx->Match(pContext); + pContext->m_nCursiveLimit ++; + return result; + } + else + return 0; + } + else + return 1; +} + +template int CDelegateElxT :: MatchNext(CContext * pContext) const +{ + if(m_pelx != 0) + return m_pelx->MatchNext(pContext); + else + return 0; +} + +// +// Empty +// +template class CEmptyElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CEmptyElxT(); +}; + +typedef CEmptyElxT <0> CEmptyElx; + +// +// Global +// +template class CGlobalElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CGlobalElxT(); +}; + +typedef CGlobalElxT <0> CGlobalElx; + +// +// Repeat +// +template class CRepeatElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CRepeatElxT(ElxInterface * pelx, int ntimes); + +protected: + int MatchFixed (CContext * pContext) const; + int MatchNextFixed(CContext * pContext) const; + int MatchForward (CContext * pContext) const + { + CContextShot shot(pContext); + + if( ! m_pelx->Match(pContext) ) + return 0; + + if(pContext->m_nCurrentPos != shot.m_nCurrentPos) + return 1; + + if( ! m_pelx->MatchNext(pContext) ) + return 0; + + if(pContext->m_nCurrentPos != shot.m_nCurrentPos) + return 1; + + shot.Restore(pContext); + return 0; + } + +public: + ElxInterface * m_pelx; + int m_nfixed; +}; + +typedef CRepeatElxT <0> CRepeatElx; + +// +// Greedy +// +template class CGreedyElxT : public CRepeatElxT +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CGreedyElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); + +protected: + int MatchVart (CContext * pContext) const; + int MatchNextVart(CContext * pContext) const; + +public: + int m_nvart; +}; + +typedef CGreedyElxT <0> CGreedyElx; + +// +// Independent +// +template class CIndependentElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CIndependentElxT(ElxInterface * pelx); + +public: + ElxInterface * m_pelx; +}; + +typedef CIndependentElxT <0> CIndependentElx; + +// +// List +// +template class CListElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CListElxT(int brightleft); + +public: + CBufferT m_elxlist; + int m_brightleft; +}; + +typedef CListElxT <0> CListElx; + +// +// Posix Elx +// +template class CPosixElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CPosixElxT(const char * posix, int brightleft); + +public: + POSIX_FUNC m_posixfun; + int m_brightleft; + int m_byes; +}; + +// +// Implementation +// +template CPosixElxT :: CPosixElxT(const char * posix, int brightleft) +{ + m_brightleft = brightleft; + + if(posix[1] == '^') + { + m_byes = 0; + posix += 2; + } + else + { + m_byes = 1; + posix += 1; + } + + if (!strncmp(posix, "alnum:", 6)) m_posixfun = ::isalnum ; + else if(!strncmp(posix, "alpha:", 6)) m_posixfun = ::isalpha ; + else if(!strncmp(posix, "ascii:", 6)) m_posixfun = ::isascii ; + else if(!strncmp(posix, "cntrl:", 6)) m_posixfun = ::iscntrl ; + else if(!strncmp(posix, "digit:", 6)) m_posixfun = ::isdigit ; + else if(!strncmp(posix, "graph:", 6)) m_posixfun = ::isgraph ; + else if(!strncmp(posix, "lower:", 6)) m_posixfun = ::islower ; + else if(!strncmp(posix, "print:", 6)) m_posixfun = ::isprint ; + else if(!strncmp(posix, "punct:", 6)) m_posixfun = ::ispunct ; + else if(!strncmp(posix, "space:", 6)) m_posixfun = ::isspace ; + else if(!strncmp(posix, "upper:", 6)) m_posixfun = ::isupper ; + else if(!strncmp(posix, "xdigit:",7)) m_posixfun = ::isxdigit; + else if(!strncmp(posix, "blank:", 6)) m_posixfun = isblank ; + else m_posixfun = 0 ; +} + +inline int isblank(int c) +{ + return c == 0x20 || c == '\t'; +} + +template int CPosixElxT :: Match(CContext * pContext) const +{ + if(m_posixfun == 0) return 0; + + int tlen = pContext->m_pMatchStringLength; + int npos = pContext->m_nCurrentPos; + + // check + int at = m_brightleft ? npos - 1 : npos; + if( at < 0 || at >= tlen ) + return 0; + + CHART ch = ((const CHART *)pContext->m_pMatchString)[at]; + + int bsucc = (*m_posixfun)(ch); + + if( ! m_byes ) + bsucc = ! bsucc; + + if( bsucc ) + pContext->m_nCurrentPos += m_brightleft ? -1 : 1; + + return bsucc; +} + +template int CPosixElxT :: MatchNext(CContext * pContext) const +{ + pContext->m_nCurrentPos -= m_brightleft ? -1 : 1; + return 0; +} + +// +// Possessive +// +template class CPossessiveElxT : public CGreedyElxT +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CPossessiveElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); +}; + +typedef CPossessiveElxT <0> CPossessiveElx; + +// +// Range Elx +// +template class CRangeElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CRangeElxT(int brightleft, int byes); + +public: + int IsContainChar(CHART ch) const; + +public: + CBufferT m_ranges; + CBufferT m_chars; + CBufferT m_embeds; + +public: + int m_brightleft; + int m_byes; +}; + +// +// Implementation +// +template CRangeElxT :: CRangeElxT(int brightleft, int byes) +{ + m_brightleft = brightleft; + m_byes = byes; +} + +template int CRangeElxT :: Match(CContext * pContext) const +{ + int tlen = pContext->m_pMatchStringLength; + int npos = pContext->m_nCurrentPos; + + // check + int at = m_brightleft ? npos - 1 : npos; + if( at < 0 || at >= tlen ) + return 0; + + CHART ch = ((const CHART *)pContext->m_pMatchString)[at]; + int bsucc = 0, i; + + // compare + for(i=0; !bsucc && iMatch(pContext)) + { + pContext->m_nCurrentPos = npos; + bsucc = 1; + } + } + + if( ! m_byes ) + bsucc = ! bsucc; + + if( bsucc ) + pContext->m_nCurrentPos += m_brightleft ? -1 : 1; + + return bsucc; +} + +template int CRangeElxT :: IsContainChar(CHART ch) const +{ + int bsucc = 0, i; + + // compare + for(i=0; !bsucc && i int CRangeElxT :: MatchNext(CContext * pContext) const +{ + pContext->m_nCurrentPos -= m_brightleft ? -1 : 1; + return 0; +} + +// +// Reluctant +// +template class CReluctantElxT : public CRepeatElxT +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CReluctantElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); + +protected: + int MatchVart (CContext * pContext) const; + int MatchNextVart(CContext * pContext) const; + +public: + int m_nvart; +}; + +typedef CReluctantElxT <0> CReluctantElx; + +// +// String Elx +// +template class CStringElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CStringElxT(const CHART * fixed, int nlength, int brightleft, int bignorecase); + +public: + CBufferT m_szPattern; + int m_brightleft; + int m_bignorecase; +}; + +// +// Implementation +// +template CStringElxT :: CStringElxT(const CHART * fixed, int nlength, int brightleft, int bignorecase) : m_szPattern(fixed, nlength) +{ + m_brightleft = brightleft; + m_bignorecase = bignorecase; +} + +template int CStringElxT :: Match(CContext * pContext) const +{ + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + int npos = pContext->m_nCurrentPos; + int tlen = pContext->m_pMatchStringLength; + int slen = m_szPattern.GetSize(); + + int bsucc; + + if(m_brightleft) + { + if(npos < slen) + return 0; + + if(m_bignorecase) + bsucc = ! m_szPattern.nCompareNoCase(pcsz + (npos - slen)); + else + bsucc = ! m_szPattern.nCompare (pcsz + (npos - slen)); + + if( bsucc ) + pContext->m_nCurrentPos -= slen; + } + else + { + if(npos + slen > tlen) + return 0; + + if(m_bignorecase) + bsucc = ! m_szPattern.nCompareNoCase(pcsz + npos); + else + bsucc = ! m_szPattern.nCompare (pcsz + npos); + + if( bsucc ) + pContext->m_nCurrentPos += slen; + } + + return bsucc; +} + +template int CStringElxT :: MatchNext(CContext * pContext) const +{ + int slen = m_szPattern.GetSize(); + + if(m_brightleft) + pContext->m_nCurrentPos += slen; + else + pContext->m_nCurrentPos -= slen; + + return 0; +} + +// +// CConditionElx +// +template class CConditionElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CConditionElxT(); + +public: + // backref condition + int m_nnumber; + CBufferT m_szNamed; + + // elx condition + ElxInterface * m_pelxask; + + // selection + ElxInterface * m_pelxyes, * m_pelxno; +}; + +template CConditionElxT :: CConditionElxT() +{ + m_nnumber = -1; +} + +template int CConditionElxT :: Match(CContext * pContext) const +{ + // status + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + + // condition result + int condition_yes = 0; + + // backref type + if( m_nnumber >= 0 ) + { + do + { + if(m_nnumber >= pContext->m_captureindex.GetSize()) break; + + int index = pContext->m_captureindex[m_nnumber]; + if( index < 0) break; + + // else valid + condition_yes = 1; + } + while(0); + } + else + { + if( m_pelxask == 0 ) + condition_yes = 1; + else + condition_yes = m_pelxask->Match(pContext); + + pContext->m_stack.Restore(nsize); + pContext->m_nCurrentPos = nbegin; + } + + // elx result + int bsucc; + if( condition_yes ) + bsucc = m_pelxyes == 0 ? 1 : m_pelxyes->Match(pContext); + else + bsucc = m_pelxno == 0 ? 1 : m_pelxno ->Match(pContext); + + if( bsucc ) + { + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(condition_yes); + } + else + { + pContext->m_capturestack.Restore(ncsize); + } + + return bsucc; +} + +template int CConditionElxT :: MatchNext(CContext * pContext) const +{ + // pop + int ncsize, condition_yes; + + pContext->m_stack.Pop(condition_yes); + pContext->m_stack.Pop(ncsize); + + // elx result + int bsucc; + if( condition_yes ) + bsucc = m_pelxyes == 0 ? 0 : m_pelxyes->MatchNext(pContext); + else + bsucc = m_pelxno == 0 ? 0 : m_pelxno ->MatchNext(pContext); + + if( bsucc ) + { + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(condition_yes); + } + else + { + pContext->m_capturestack.Restore(ncsize); + } + + return bsucc; +} + +// +// MatchResult +// +template class MatchResultT +{ +public: + int IsMatched() const; + +public: + int GetStart() const; + int GetEnd () const; + +public: + int MaxGroupNumber() const; + int GetGroupStart(int nGroupNumber) const; + int GetGroupEnd (int nGroupNumber) const; + +public: + MatchResultT(const MatchResultT & from) { *this = from; } + MatchResultT(CContext * pContext = 0, int nMaxNumber = -1); + MatchResultT & operator = (const MatchResultT &); + inline operator int() const { return IsMatched(); } + +public: + CBufferT m_result; +}; + +typedef MatchResultT <0> MatchResult; + +// Stocked Elx IDs +enum STOCKELX_ID_DEFINES +{ + STOCKELX_EMPTY = 0, + + /////////////////////// + + STOCKELX_DOT_ALL, + STOCKELX_DOT_NOT_ALL, + + STOCKELX_WORD, + STOCKELX_WORD_NOT, + + STOCKELX_SPACE, + STOCKELX_SPACE_NOT, + + STOCKELX_DIGITAL, + STOCKELX_DIGITAL_NOT, + + ////////////////////// + + STOCKELX_DOT_ALL_RIGHTLEFT, + STOCKELX_DOT_NOT_ALL_RIGHTLEFT, + + STOCKELX_WORD_RIGHTLEFT, + STOCKELX_WORD_RIGHTLEFT_NOT, + + STOCKELX_SPACE_RIGHTLEFT, + STOCKELX_SPACE_RIGHTLEFT_NOT, + + STOCKELX_DIGITAL_RIGHTLEFT, + STOCKELX_DIGITAL_RIGHTLEFT_NOT, + + ///////////////////// + + STOCKELX_COUNT +}; + +// REGEX_FLAGS +#ifndef _REGEX_FLAGS_DEFINED + enum REGEX_FLAGS + { + NO_FLAG = 0, + SINGLELINE = 0x01, + MULTILINE = 0x02, + GLOBAL = 0x04, + IGNORECASE = 0x08, + RIGHTTOLEFT = 0x10, + EXTENDED = 0x20 + }; + #define _REGEX_FLAGS_DEFINED +#endif + +// +// Builder T +// +template class CBuilderT +{ +public: + typedef CDelegateElxT CDelegateElx; + typedef CBracketElxT CBracketElx; + typedef CBackrefElxT CBackrefElx; + typedef CConditionElxT CConditionElx; + +// Methods +public: + ElxInterface * Build(const CBufferRefT & pattern, int flags); + int GetNamedNumber(const CBufferRefT & named) const; + void Clear(); + +public: + CBuilderT(); + ~CBuilderT(); + +// Public Attributes +public: + ElxInterface * m_pTopElx; + int m_nFlags; + int m_nMaxNumber; + int m_nNextNamed; + int m_nGroupCount; + + CBufferT m_objlist; + CBufferT m_grouplist; + CBufferT m_recursivelist; + CBufferT m_namedlist; + CBufferT m_namedbackreflist; + CBufferT m_namedconditionlist; + +// CHART_INFO +protected: + struct CHART_INFO + { + public: + CHART ch; + int type; + int pos; + int len; + + public: + CHART_INFO(CHART c, int t, int p = 0, int l = 0) { ch = c; type = t; pos = p; len = l; } + inline int operator == (const CHART_INFO & ci) { return ch == ci.ch && type == ci.type; } + inline int operator != (const CHART_INFO & ci) { return ! operator == (ci); } + }; + +protected: + static unsigned int Hex2Int(const CHART * pcsz, int length, int & used); + static int ReadDec(char * & str, unsigned int & dec); + void MoveNext(); + int GetNext2(); + + ElxInterface * BuildAlternative(int vaflags); + ElxInterface * BuildList (int & flags); + ElxInterface * BuildRepeat (int & flags); + ElxInterface * BuildSimple (int & flags); + ElxInterface * BuildCharset (int & flags); + ElxInterface * BuildRecursive (int & flags); + ElxInterface * BuildBoundary (int & flags); + ElxInterface * BuildBackref (int & flags); + + ElxInterface * GetStockElx (int nStockId); + ElxInterface * Keep(ElxInterface * pElx); + +// Private Attributes +protected: + CBufferRefT m_pattern; + CHART_INFO prev, curr, next, nex2; + int m_nNextPos; + int m_nCharsetDepth; + int m_bQuoted; + POSIX_FUNC m_quote_fun; + + // Backup current pos + struct Snapshot + { + CHART_INFO prev, curr, next, nex2; + int m_nNextPos; + int m_nCharsetDepth; + int m_bQuoted; + POSIX_FUNC m_quote_fun; + Snapshot():prev(0,0),curr(0,0),next(0,0),nex2(0,0) {} + }; + void Backup (Snapshot * pdata) { memcpy(pdata, &prev, sizeof(Snapshot)); } + void Restore(Snapshot * pdata) { memcpy(&prev, pdata, sizeof(Snapshot)); } + + ElxInterface * m_pStockElxs[STOCKELX_COUNT]; +}; + +// +// Implementation +// +template CBuilderT :: CBuilderT() : m_pattern(0, 0), prev(0, 0), curr(0, 0), next(0, 0), nex2(0, 0) +{ + Clear(); +} + +template CBuilderT :: ~CBuilderT() +{ + Clear(); +} + +template int CBuilderT :: GetNamedNumber(const CBufferRefT & named) const +{ + for(int i=0; im_elxlist[0])->m_szNamed.CompareNoCase(named) ) + return ((CBracketElx *)m_namedlist[i]->m_elxlist[0])->m_nnumber; + } + + return -3; +} + +template ElxInterface * CBuilderT :: Build(const CBufferRefT & pattern, int flags) +{ + // init + m_pattern = pattern; + m_nNextPos = 0; + m_nCharsetDepth = 0; + m_nMaxNumber = 0; + m_nNextNamed = 0; + m_nFlags = flags; + m_bQuoted = 0; + m_quote_fun = 0; + + m_grouplist .Restore(0); + m_recursivelist .Restore(0); + m_namedlist .Restore(0); + m_namedbackreflist .Restore(0); + m_namedconditionlist.Restore(0); + + int i; + for(i=0; i<3; i++) MoveNext(); + + // build + m_pTopElx = BuildAlternative(flags); + + // group 0 + m_grouplist.Prepare(0); + m_grouplist[0] = m_pTopElx; + + // append named to unnamed + m_nGroupCount = m_grouplist.GetSize(); + + m_grouplist.Prepare(m_nMaxNumber + m_namedlist.GetSize()); + + for(i=0; im_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_namedlist[i]->m_elxlist[2]; + + // append + m_grouplist[m_nGroupCount ++] = m_namedlist[i]; + + if( pleft->m_nnumber > 0 ) + continue; + + // same name + int find_same_name = GetNamedNumber(pleft->m_szNamed); + if( find_same_name >= 0 ) + { + pleft ->m_nnumber = find_same_name; + pright->m_nnumber = find_same_name; + } + else + { + m_nMaxNumber ++; + + pleft ->m_nnumber = m_nMaxNumber; + pright->m_nnumber = m_nMaxNumber; + } + } + + for(i=1; im_elxlist[0]; + + if( pleft->m_nnumber > m_nMaxNumber ) + m_nMaxNumber = pleft->m_nnumber; + } + + // connect recursive + for(i=0; im_ndata == -3 ) + m_recursivelist[i]->m_ndata = GetNamedNumber(m_recursivelist[i]->m_szNamed); + + if( m_recursivelist[i]->m_ndata >= 0 && m_recursivelist[i]->m_ndata <= m_nMaxNumber ) + { + if( m_recursivelist[i]->m_ndata == 0 ) + m_recursivelist[i]->m_pelx = m_pTopElx; + else for(int j=1; jm_ndata == ((CBracketElx *)((CListElx*)m_grouplist[j])->m_elxlist[0])->m_nnumber) + { + m_recursivelist[i]->m_pelx = m_grouplist[j]; + break; + } + } + } + } + + // named backref + for(i=0; im_nnumber = GetNamedNumber(m_namedbackreflist[i]->m_szNamed); + } + + // named condition + for(i=0; im_szNamed); + if( nn >= 0 ) + { + m_namedconditionlist[i]->m_nnumber = nn; + m_namedconditionlist[i]->m_pelxask = 0; + } + } + + return m_pTopElx; +} + +template void CBuilderT :: Clear() +{ + for(int i=0; i unsigned int CBuilderT :: Hex2Int(const CHART * pcsz, int length, int & used) +{ + unsigned int result = 0; + int & i = used; + + for(i=0; i= RCHART('0') && pcsz[i] <= RCHART('9')) + result = (result << 4) + (pcsz[i] - RCHART('0')); + else if(pcsz[i] >= RCHART('A') && pcsz[i] <= RCHART('F')) + result = (result << 4) + (0x0A + (pcsz[i] - RCHART('A'))); + else if(pcsz[i] >= RCHART('a') && pcsz[i] <= RCHART('f')) + result = (result << 4) + (0x0A + (pcsz[i] - RCHART('a'))); + else + break; + } + + return result; +} + +template inline ElxInterface * CBuilderT :: Keep(ElxInterface * pelx) +{ + m_objlist.Push(pelx); + return pelx; +} + +template void CBuilderT :: MoveNext() +{ + // forwards + prev = curr; + curr = next; + next = nex2; + + // get nex2 + while( ! GetNext2() ) {}; +} + +template int CBuilderT :: GetNext2() +{ + // check length + if(m_nNextPos >= m_pattern.GetSize()) + { + nex2 = CHART_INFO(0, 1, m_nNextPos, 0); + return 1; + } + + int delta = 1; + CHART ch = m_pattern[m_nNextPos]; + + // if quoted + if(m_bQuoted) + { + if(ch == RCHART('\\')) + { + if(m_pattern[m_nNextPos + 1] == RCHART('E')) + { + m_quote_fun = 0; + m_bQuoted = 0; + m_nNextPos += 2; + return 0; + } + } + + if(m_quote_fun != 0) + nex2 = CHART_INFO((CHART)(*m_quote_fun)((int)ch), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + + m_nNextPos += delta; + + return 1; + } + + // common + switch(ch) + { + case RCHART('\\'): + { + CHART ch1 = m_pattern[m_nNextPos+1]; + + // backref + if(ch1 >= RCHART('0') && ch1 <= RCHART('9')) + { + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + break; + } + + // escape + delta = 2; + + switch(ch1) + { + case RCHART('A'): + case RCHART('Z'): + case RCHART('z'): + case RCHART('w'): + case RCHART('W'): + case RCHART('s'): + case RCHART('S'): + case RCHART('B'): + case RCHART('d'): + case RCHART('D'): + case RCHART('k'): + case RCHART('g'): + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + + case RCHART('b'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO('\b', 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + + /* + case RCHART('<'): + case RCHART('>'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + */ + + case RCHART('x'): + if(m_pattern[m_nNextPos+2] != '{') + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 2, red); + + delta += red; + + if(red > 0) + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + + break; + } + + case RCHART('u'): + if(m_pattern[m_nNextPos+2] != '{') + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 4, red); + + delta += red; + + if(red > 0) + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + } + else + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 3, sizeof(int) * 2, red); + + delta += red; + + while(m_nNextPos + delta < m_pattern.GetSize() && m_pattern.At(m_nNextPos + delta) != RCHART('}')) + delta ++; + + delta ++; // skip '}' + + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + } + break; + + case RCHART('a'): nex2 = CHART_INFO(RCHART('\a'), 0, m_nNextPos, delta); break; + case RCHART('f'): nex2 = CHART_INFO(RCHART('\f'), 0, m_nNextPos, delta); break; + case RCHART('n'): nex2 = CHART_INFO(RCHART('\n'), 0, m_nNextPos, delta); break; + case RCHART('r'): nex2 = CHART_INFO(RCHART('\r'), 0, m_nNextPos, delta); break; + case RCHART('t'): nex2 = CHART_INFO(RCHART('\t'), 0, m_nNextPos, delta); break; + case RCHART('v'): nex2 = CHART_INFO(RCHART('\v'), 0, m_nNextPos, delta); break; + case RCHART('e'): nex2 = CHART_INFO(RCHART( 27 ), 0, m_nNextPos, delta); break; + + case RCHART('G'): // skip '\G' + if(m_nCharsetDepth > 0) + { + m_nNextPos += 2; + return 0; + } + else + { + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + } + + case RCHART('L'): + if( ! m_quote_fun ) m_quote_fun = ::tolower; + + case RCHART('U'): + if( ! m_quote_fun ) m_quote_fun = ::toupper; + + case RCHART('Q'): + { + m_bQuoted = 1; + m_nNextPos += 2; + return 0; + } + + case RCHART('E'): + { + m_quote_fun = 0; + m_bQuoted = 0; + m_nNextPos += 2; + return 0; + } + + case 0: + if(m_nNextPos+1 >= m_pattern.GetSize()) + { + delta = 1; + nex2 = CHART_INFO(ch , 0, m_nNextPos, delta); + } + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); // common '\0' char + break; + + default: + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + break; + } + } + break; + + case RCHART('*'): + case RCHART('+'): + case RCHART('?'): + case RCHART('.'): + case RCHART('{'): + case RCHART('}'): + case RCHART(')'): + case RCHART('|'): + case RCHART('$'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + break; + + case RCHART('-'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case RCHART('('): + { + CHART ch1 = m_pattern[m_nNextPos+1]; + CHART ch2 = m_pattern[m_nNextPos+2]; + + // skip remark + if(ch1 == RCHART('?') && ch2 == RCHART('#')) + { + m_nNextPos += 2; + while(m_nNextPos < m_pattern.GetSize()) + { + if(m_pattern[m_nNextPos] == RCHART(')')) + break; + + m_nNextPos ++; + } + + if(m_pattern[m_nNextPos] == RCHART(')')) + { + m_nNextPos ++; + + // get next nex2 + return 0; + } + } + else + { + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + } + break; + + case RCHART('#'): + if(m_nFlags & EXTENDED) + { + // skip remark + m_nNextPos ++; + + while(m_nNextPos < m_pattern.GetSize()) + { + if(m_pattern[m_nNextPos] == RCHART('\n') || m_pattern[m_nNextPos] == RCHART('\r')) + break; + + m_nNextPos ++; + } + + // get next nex2 + return 0; + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(' '): + case RCHART('\f'): + case RCHART('\n'): + case RCHART('\r'): + case RCHART('\t'): + case RCHART('\v'): + if(m_nFlags & EXTENDED) + { + m_nNextPos ++; + + // get next nex2 + return 0; + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART('['): + if( m_nCharsetDepth == 0 || m_pattern.At(m_nNextPos + 1, 0) == RCHART(':') ) + { + m_nCharsetDepth ++; + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(']'): + if(m_nCharsetDepth > 0) + { + m_nCharsetDepth --; + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(':'): + if(next == CHART_INFO(RCHART('['), 1)) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case RCHART('^'): + if(m_nCharsetDepth == 0 || next == CHART_INFO(RCHART('['), 1) || (curr == CHART_INFO(RCHART('['), 1) && next == CHART_INFO(RCHART(':'), 1))) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case 0: + if(m_nNextPos >= m_pattern.GetSize()) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); // end of string + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); // common '\0' char + break; + + default: + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + } + + m_nNextPos += delta; + + return 1; +} + +template ElxInterface * CBuilderT :: GetStockElx(int nStockId) +{ + ElxInterface ** pStockElxs = m_pStockElxs; + + // check + if(nStockId < 0 || nStockId >= STOCKELX_COUNT) + return GetStockElx(0); + + // create if no + if(pStockElxs[nStockId] == 0) + { + switch(nStockId) + { + case STOCKELX_EMPTY: + pStockElxs[nStockId] = Keep(new CEmptyElx()); + break; + + case STOCKELX_WORD: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DOT_ALL: + pStockElxs[nStockId] = Keep(new CRangeElxT (0, 0)); + break; + + case STOCKELX_DOT_NOT_ALL: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DOT_ALL_RIGHTLEFT: + pStockElxs[nStockId] = Keep(new CRangeElxT (1, 0)); + break; + + case STOCKELX_DOT_NOT_ALL_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + pRange->m_chars .Push(RCHART('\f')); + pRange->m_chars .Push(RCHART('\v')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + pRange->m_chars .Push(RCHART('\f')); + pRange->m_chars .Push(RCHART('\v')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + } + } + + // return + return pStockElxs[nStockId]; +} + +template ElxInterface * CBuilderT :: BuildAlternative(int vaflags) +{ + if(curr == CHART_INFO(0, 1)) + return GetStockElx(STOCKELX_EMPTY); + + // flag instance + int flags = vaflags; + + // first part + ElxInterface * pAlternativeOne = BuildList(flags); + + // check alternative + if(curr == CHART_INFO(RCHART('|'), 1)) + { + CAlternativeElx * pAlternative = (CAlternativeElx *)Keep(new CAlternativeElx()); + pAlternative->m_elxlist.Push(pAlternativeOne); + + // loop + while(curr == CHART_INFO(RCHART('|'), 1)) + { + // skip '|' itself + MoveNext(); + + pAlternativeOne = BuildList(flags); + pAlternative->m_elxlist.Push(pAlternativeOne); + } + + return pAlternative; + } + + return pAlternativeOne; +} + +template ElxInterface * CBuilderT :: BuildList(int & flags) +{ + if(curr == CHART_INFO(0, 1) || curr == CHART_INFO(RCHART('|'), 1) || curr == CHART_INFO(RCHART(')'), 1)) + return GetStockElx(STOCKELX_EMPTY); + + // first + ElxInterface * pListOne = BuildRepeat(flags); + + if(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1)) + { + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + pList->m_elxlist.Push(pListOne); + + while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1)) + { + pListOne = BuildRepeat(flags); + + // add + pList->m_elxlist.Push(pListOne); + } + + return pList; + } + + return pListOne; +} + +template ElxInterface * CBuilderT :: BuildRepeat(int & flags) +{ + // simple + ElxInterface * pSimple = BuildSimple(flags); + + if(curr.type == 0) return pSimple; + + // is quantifier or not + int bIsQuantifier = 1; + + // quantifier range + unsigned int nMin = 0, nMax = 0; + + switch(curr.ch) + { + case RCHART('{'): + { + CBufferT re; + + // skip '{' + MoveNext(); + + // copy + while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('}'), 1)) + { + re.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + // skip '}' + MoveNext(); + + // read + int red; + char * str = re.GetBuffer(); + + if( ! ReadDec(str, nMin) ) + red = 0; + else if( *str != ',' ) + red = 1; + else + { + str ++; + + if( ! ReadDec(str, nMax) ) + red = 2; + else + red = 3; + } + + // check + if(red <= 1 ) nMax = nMin; + if(red == 2 ) nMax = INT_MAX; + if(nMax < nMin) nMax = nMin; + } + break; + + case RCHART('?'): + nMin = 0; + nMax = 1; + + // skip '?' + MoveNext(); + break; + + case RCHART('*'): + nMin = 0; + nMax = INT_MAX; + + // skip '*' + MoveNext(); + break; + + case RCHART('+'): + nMin = 1; + nMax = INT_MAX; + + // skip '+' + MoveNext(); + break; + + default: + bIsQuantifier = 0; + break; + } + + // do quantify + if(bIsQuantifier) + { + // 0 times + if(nMax == 0) + return GetStockElx(STOCKELX_EMPTY); + + // fixed times + if(nMin == nMax) + { + if(curr == CHART_INFO(RCHART('?'), 1) || curr == CHART_INFO(RCHART('+'), 1)) + MoveNext(); + + return Keep(new CRepeatElx(pSimple, nMin)); + } + + // range times + if(curr == CHART_INFO(RCHART('?'), 1)) + { + MoveNext(); + return Keep(new CReluctantElx(pSimple, nMin, nMax)); + } + else if(curr == CHART_INFO(RCHART('+'), 1)) + { + MoveNext(); + return Keep(new CPossessiveElx(pSimple, nMin, nMax)); + } + else + { + return Keep(new CGreedyElx(pSimple, nMin, nMax)); + } + } + + return pSimple; +} + +template ElxInterface * CBuilderT :: BuildSimple(int & flags) +{ + CBufferT fixed; + + while(curr != CHART_INFO(0, 1)) + { + if(curr.type == 0) + { + if(next == CHART_INFO(RCHART('{'), 1) || next == CHART_INFO(RCHART('?'), 1) || next == CHART_INFO(RCHART('*'), 1) || next == CHART_INFO(RCHART('+'), 1)) + { + if(fixed.GetSize() == 0) + { + fixed.Append(curr.ch, 1); + MoveNext(); + } + + break; + } + else + { + fixed.Append(curr.ch, 1); + MoveNext(); + } + } + else if(curr.type == 1) + { + CHART vch = curr.ch; + + // end of simple + if(vch == RCHART(')') || vch == RCHART('|')) + break; + + // has fixed already + if(fixed.GetSize() > 0) + break; + + // left parentheses + if(vch == RCHART('(')) + { + return BuildRecursive(flags); + } + + // char set + if( vch == RCHART('[') || vch == RCHART('.') || vch == RCHART('w') || vch == RCHART('W') || + vch == RCHART('s') || vch == RCHART('S') || vch == RCHART('d') || vch == RCHART('D') + ) + { + return BuildCharset(flags); + } + + // boundary + if( vch == RCHART('^') || vch == RCHART('$') || vch == RCHART('A') || vch == RCHART('Z') || vch == RCHART('z') || + vch == RCHART('b') || vch == RCHART('B') || vch == RCHART('G') // vch == RCHART('<') || vch == RCHART('>') + ) + { + return BuildBoundary(flags); + } + + // backref + if(vch == RCHART('\\') || vch == RCHART('k') || vch == RCHART('g')) + { + return BuildBackref(flags); + } + + // treat vchar as char + fixed.Append(curr.ch, 1); + MoveNext(); + } + } + + if(fixed.GetSize() > 0) + return Keep(new CStringElxT (fixed.GetBuffer(), fixed.GetSize(), flags & RIGHTTOLEFT, flags & IGNORECASE)); + else + return GetStockElx(STOCKELX_EMPTY); +} + +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +template ElxInterface * CBuilderT :: BuildCharset(int & flags) +{ + // char + CHART ch = curr.ch; + + // skip + MoveNext(); + + switch(ch) + { + case RCHART('.'): + return GetStockElx( + flags & RIGHTTOLEFT ? + ((flags & SINGLELINE) ? STOCKELX_DOT_ALL_RIGHTLEFT : STOCKELX_DOT_NOT_ALL_RIGHTLEFT) : + ((flags & SINGLELINE) ? STOCKELX_DOT_ALL : STOCKELX_DOT_NOT_ALL) + ); + + case RCHART('w'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_WORD_RIGHTLEFT : STOCKELX_WORD); + + case RCHART('W'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_WORD_RIGHTLEFT_NOT : STOCKELX_WORD_NOT); + + case RCHART('s'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_SPACE_RIGHTLEFT : STOCKELX_SPACE); + + case RCHART('S'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_SPACE_RIGHTLEFT_NOT : STOCKELX_SPACE_NOT); + + case RCHART('d'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_DIGITAL_RIGHTLEFT : STOCKELX_DIGITAL); + + case RCHART('D'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_DIGITAL_RIGHTLEFT_NOT : STOCKELX_DIGITAL_NOT); + + case RCHART('['): + { + CRangeElxT * pRange; + + // create + if(curr == CHART_INFO(RCHART(':'), 1)) + { + // Backup before posix + Snapshot shot; + Backup(&shot); + + CBufferT posix; + + do { + posix.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + while(curr.ch != RCHART(0) && curr != CHART_INFO(RCHART(']'), 1)); + + MoveNext(); // skip ']' + + // posix + CPosixElxT * pposix = (CPosixElxT *) Keep(new CPosixElxT (posix.GetBuffer(), flags & RIGHTTOLEFT)); + if(pposix->m_posixfun != 0) + { + return pposix; + } + + // restore if not posix + Restore(&shot); + } + + if(curr == CHART_INFO(RCHART('^'), 1)) + { + MoveNext(); // skip '^' + pRange = (CRangeElxT *)Keep(new CRangeElxT (flags & RIGHTTOLEFT, 0)); + } + else + { + pRange = (CRangeElxT *)Keep(new CRangeElxT (flags & RIGHTTOLEFT, 1)); + } + + // parse + while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART(']'), 1)) + { + ch = curr.ch; + + if(curr.type == 1 && ( + ch == RCHART('.') || ch == RCHART('w') || ch == RCHART('W') || ch == RCHART('s') || ch == RCHART('S') || ch == RCHART('d') || ch == RCHART('D') || + (ch == RCHART('[') && next == CHART_INFO(RCHART(':'), 1)) + )) + { + pRange->m_embeds.Push(BuildCharset(flags)); + } + else if(next == CHART_INFO(RCHART('-'), 1) && nex2.type == 0) + { + pRange->m_ranges.Push(ch); pRange->m_ranges.Push(nex2.ch); + + // next + MoveNext(); + MoveNext(); + MoveNext(); + } + else + { + pRange->m_chars.Push(ch); + + // next + MoveNext(); + } + } + + // skip ']' + MoveNext(); + + if( flags & IGNORECASE ) + { + CBufferT & ranges = pRange->m_ranges; + int i, oldcount = ranges.GetSize() / 2; + + for(i=0; i= RCHART('A') ) + { + newmin = tolower( max(RCHART('A'), ranges[i*2 ]) ); + newmax = tolower( min(RCHART('Z'), ranges[i*2+1]) ); + + if( newmin < ranges[i*2] || newmax > ranges[i*2+1] ) + { + ranges.Push(newmin); + ranges.Push(newmax); + } + } + + if( ranges[i*2] <= RCHART('z') && ranges[i*2+1] >= RCHART('a') ) + { + newmin = toupper( max(RCHART('a'), ranges[i*2 ]) ); + newmax = toupper( min(RCHART('z'), ranges[i*2+1]) ); + + if( newmin < ranges[i*2] || newmax > ranges[i*2+1] ) + { + ranges.Push(newmin); + ranges.Push(newmax); + } + } + } + + CBufferT & chars = pRange->m_chars; + oldcount = chars.GetSize(); + for(i=0; iIsContainChar(tolower(chars[i])) ) + chars.Push(tolower(chars[i])); + + if( islower(chars[i]) && ! pRange->IsContainChar(toupper(chars[i])) ) + chars.Push(toupper(chars[i])); + } + } + + return pRange; + } + } + + return GetStockElx(STOCKELX_EMPTY); +} + +template ElxInterface * CBuilderT :: BuildRecursive(int & flags) +{ + // skip '(' + MoveNext(); + + if(curr == CHART_INFO(RCHART('?'), 1)) + { + ElxInterface * pElx = 0; + + // skip '?' + MoveNext(); + + int bNegative = 0; + CHART named_end = RCHART('>'); + + switch(curr.ch) + { + case RCHART('!'): + bNegative = 1; + + case RCHART('='): + { + MoveNext(); // skip '!' or '=' + pElx = Keep(new CAssertElx(BuildAlternative(flags & ~RIGHTTOLEFT), !bNegative)); + } + break; + + case RCHART('<'): + switch(next.ch) + { + case RCHART('!'): + bNegative = 1; + + case RCHART('='): + MoveNext(); // skip '<' + MoveNext(); // skip '!' or '=' + { + pElx = Keep(new CAssertElx(BuildAlternative(flags | RIGHTTOLEFT), !bNegative)); + } + break; + + default: // named group + break; + } + // break if assertion // else named + if(pElx != 0) break; + + case RCHART('P'): + if(curr.ch == RCHART('P')) MoveNext(); // skip 'P' + + case RCHART('\''): + if (curr.ch == RCHART('<' )) named_end = RCHART('>' ); + else if(curr.ch == RCHART('\'')) named_end = RCHART('\''); + MoveNext(); // skip '<' or '\'' + { + // named number + int nThisBackref = m_nNextNamed ++; + + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + CBracketElx * pleft = (CBracketElx *)Keep(new CBracketElx(-1, flags & RIGHTTOLEFT ? 1 : 0)); + CBracketElx * pright = (CBracketElx *)Keep(new CBracketElx(-1, flags & RIGHTTOLEFT ? 0 : 1)); + + // save name + CBufferT & name = pleft->m_szNamed; + CBufferT num; + + while(curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pleft ->m_nnumber = number; + pright->m_nnumber = number; + + name.Release(); + } + + // left, center, right + pList->m_elxlist.Push(pleft); + pList->m_elxlist.Push(BuildAlternative(flags)); + pList->m_elxlist.Push(pright); + + // for recursive + m_namedlist.Prepare(nThisBackref); + m_namedlist[nThisBackref] = pList; + + pElx = pList; + } + break; + + case RCHART('>'): + { + MoveNext(); // skip '>' + pElx = Keep(new CIndependentElx(BuildAlternative(flags))); + } + break; + + case RCHART('R'): + MoveNext(); // skip 'R' + while(curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space + + if(curr.ch == RCHART('<') || curr.ch == RCHART('\'')) + { + named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\''); + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(-3)); + + MoveNext(); // skip '<' or '\\' + + // save name + CBufferT & name = pDelegate->m_szNamed; + CBufferT num; + + while(curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pDelegate->m_ndata = number; + name.Release(); + } + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + else + { + CBufferT rto; + while(curr.ch != RCHART(0) && curr.ch != RCHART(')')) + { + rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + unsigned int rtono = 0; + char * str = rto.GetBuffer(); + ReadDec(str, rtono); + + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono)); + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + break; + + case RCHART('('): + { + CConditionElx * pConditionElx = (CConditionElx *)Keep(new CConditionElx()); + + // condition + ElxInterface * & pCondition = pConditionElx->m_pelxask; + + if(next == CHART_INFO(RCHART('?'), 1)) + { + pCondition = BuildRecursive(flags); + } + else // named, assert or number + { + MoveNext(); // skip '(' + int pos0 = curr.pos; + + // save elx condition + pCondition = Keep(new CAssertElx(BuildAlternative(flags), 1)); + + // save name + pConditionElx->m_szNamed.Append(m_pattern.GetBuffer() + pos0, curr.pos - pos0, 1); + + // save number + CBufferT numstr; + while(pos0 < curr.pos) + { + CHART ch = m_pattern[pos0]; + numstr.Append(((ch & (CHART)0xff) == ch) ? (char)ch : 0, 1); + pos0 ++; + } + + unsigned int number; + char * str = numstr.GetBuffer(); + + // valid group number + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pConditionElx->m_nnumber = number; + pCondition = 0; + } + else // maybe elx, maybe named + { + pConditionElx->m_nnumber = -1; + m_namedconditionlist.Push(pConditionElx); + } + + MoveNext(); // skip ')' + } + + // alternative + { + int newflags = flags; + + pConditionElx->m_pelxyes = BuildList(newflags); + } + + if(curr.ch == RCHART('|')) + { + MoveNext(); // skip '|' + + pConditionElx->m_pelxno = BuildAlternative(flags); + } + else + { + pConditionElx->m_pelxno = 0; + } + + pElx = pConditionElx; + } + break; + + default: + while(curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space + + if(curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) // recursive (?1) => (?R1) + { + CBufferT rto; + while(curr.ch != RCHART(0) && curr.ch != RCHART(')')) + { + rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + unsigned int rtono = 0; + char * str = rto.GetBuffer(); + ReadDec(str, rtono); + + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono)); + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + else + { + // flag + int newflags = flags; + while(curr != CHART_INFO(0, 1) && curr.ch != RCHART(':') && curr.ch != RCHART(')') && curr != CHART_INFO(RCHART('('), 1)) + { + int tochange = 0; + + switch(curr.ch) + { + case RCHART('i'): + case RCHART('I'): + tochange = IGNORECASE; + break; + + case RCHART('s'): + case RCHART('S'): + tochange = SINGLELINE; + break; + + case RCHART('m'): + case RCHART('M'): + tochange = MULTILINE; + break; + + case RCHART('g'): + case RCHART('G'): + tochange = GLOBAL; + break; + + case RCHART('-'): + bNegative = 1; + break; + } + + if(bNegative) + newflags &= ~tochange; + else + newflags |= tochange; + + // move to next char + MoveNext(); + } + + if(curr.ch == RCHART(':') || curr == CHART_INFO(RCHART('('), 1)) + { + // skip ':' + if(curr.ch == RCHART(':')) MoveNext(); + + pElx = BuildAlternative(newflags); + } + else + { + // change parent flags + flags = newflags; + + pElx = GetStockElx(STOCKELX_EMPTY); + } + } + break; + } + + MoveNext(); // skip ')' + + return pElx; + } + else + { + // group and number + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + int nThisBackref = ++ m_nMaxNumber; + + // left, center, right + pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, flags & RIGHTTOLEFT ? 1 : 0))); + pList->m_elxlist.Push(BuildAlternative(flags)); + pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, flags & RIGHTTOLEFT ? 0 : 1))); + + // for recursive + m_grouplist.Prepare(nThisBackref); + m_grouplist[nThisBackref] = pList; + + // right + MoveNext(); // skip ')' + + return pList; + } +} + +template ElxInterface * CBuilderT :: BuildBoundary(int & flags) +{ + // char + CHART ch = curr.ch; + + // skip + MoveNext(); + + switch(ch) + { + case RCHART('^'): + return Keep(new CBoundaryElxT ((flags & MULTILINE) ? BOUNDARY_LINE_BEGIN : BOUNDARY_FILE_BEGIN)); + + case RCHART('$'): + return Keep(new CBoundaryElxT ((flags & MULTILINE) ? BOUNDARY_LINE_END : BOUNDARY_FILE_END)); + + case RCHART('b'): + return Keep(new CBoundaryElxT (BOUNDARY_WORD_EDGE)); + + case RCHART('B'): + return Keep(new CBoundaryElxT (BOUNDARY_WORD_EDGE, 0)); + + case RCHART('A'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_BEGIN)); + + case RCHART('Z'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_END_N)); + + case RCHART('z'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_END)); + + case RCHART('G'): + if(flags & GLOBAL) + return Keep(new CGlobalElx()); + else + return GetStockElx(STOCKELX_EMPTY); + + default: + return GetStockElx(STOCKELX_EMPTY); + } +} + +template ElxInterface * CBuilderT :: BuildBackref(int & flags) +{ + // skip '\\' or '\k' or '\g' + MoveNext(); + + if(curr.ch == RCHART('<') || curr.ch == RCHART('\'')) + { + CHART named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\''); + CBackrefElxT * pbackref = (CBackrefElxT *)Keep(new CBackrefElxT (-1, flags & RIGHTTOLEFT, flags & IGNORECASE)); + + MoveNext(); // skip '<' or '\'' + + // save name + CBufferT & name = pbackref->m_szNamed; + CBufferT num; + + while(curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pbackref->m_nnumber = number; + name.Release(); + } + else + { + m_namedbackreflist.Push(pbackref); + } + + return pbackref; + } + else + { + unsigned int nbackref = 0; + + for(int i=0; i<3; i++) + { + if(curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) + nbackref = nbackref * 10 + (curr.ch - RCHART('0')); + else + break; + + MoveNext(); + } + + return Keep(new CBackrefElxT (nbackref, flags & RIGHTTOLEFT, flags & IGNORECASE)); + } +} + +template int CBuilderT :: ReadDec(char * & str, unsigned int & dec) +{ + int s = 0; + while(str[s] != 0 && isspace(str[s])) s++; + + if(str[s] < '0' || str[s] > '9') return 0; + + dec = 0; + unsigned int i; + + for(i = s; i= '0' && str[i] <= '9') + dec = dec * 10 + (str[i] - '0'); + else + break; + } + + while(str[i] != 0 && isspace(str[i])) i++; + str += i; + + return 1; +} + +// +// Regexp +// +template class CRegexpT +{ +public: + CRegexpT(const CHART * pattern = 0, int flags = 0); + CRegexpT(const CHART * pattern, int length, int flags); + void Compile(const CHART * pattern, int flags = 0); + void Compile(const CHART * pattern, int length, int flags); + +public: + MatchResult MatchExact(const CHART * tstring, CContext * pContext = 0) const; + MatchResult MatchExact(const CHART * tstring, int length, CContext * pContext = 0) const; + MatchResult Match(const CHART * tstring, int start = -1, CContext * pContext = 0) const; + MatchResult Match(const CHART * tstring, int length, int start, CContext * pContext = 0) const; + MatchResult Match(CContext * pContext) const; + CContext * PrepareMatch(const CHART * tstring, int start = -1, CContext * pContext = 0) const; + CContext * PrepareMatch(const CHART * tstring, int length, int start, CContext * pContext = 0) const; + CHART * Replace(const CHART * tstring, const CHART * replaceto, int start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const; + CHART * Replace(const CHART * tstring, int string_length, const CHART * replaceto, int to_length, int & result_length, int start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const; + int GetNamedGroupNumber(const CHART * group_name) const; + +public: + static void ReleaseString (CHART * tstring ); + static void ReleaseContext(CContext * pContext); + +public: + CBuilderT m_builder; +}; + +// +// Implementation +// +template CRegexpT :: CRegexpT(const CHART * pattern, int flags) +{ + Compile(pattern, CBufferRefT(pattern).GetSize(), flags); +} + +template CRegexpT :: CRegexpT(const CHART * pattern, int length, int flags) +{ + Compile(pattern, length, flags); +} + +template inline void CRegexpT :: Compile(const CHART * pattern, int flags) +{ + Compile(pattern, CBufferRefT(pattern).GetSize(), flags); +} + +template void CRegexpT :: Compile(const CHART * pattern, int length, int flags) +{ + m_builder.Clear(); + if(pattern != 0) m_builder.Build(CBufferRefT(pattern, length), flags); +} + +template inline MatchResult CRegexpT :: MatchExact(const CHART * tstring, CContext * pContext) const +{ + return MatchExact(tstring, CBufferRefT(tstring).GetSize(), pContext); +} + +template MatchResult CRegexpT :: MatchExact(const CHART * tstring, int length, CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + // info + int endpos = 0; + + CContext context; + if(pContext == 0) pContext = &context; + + pContext->m_stack.Restore(0); + pContext->m_capturestack.Restore(0); + pContext->m_captureindex.Restore(0); + + pContext->m_nParenZindex = 0; + pContext->m_nLastBeginPos = -1; + pContext->m_pMatchString = (void*)tstring; + pContext->m_pMatchStringLength = length; + pContext->m_nCursiveLimit = 100; + + if(m_builder.m_nFlags & RIGHTTOLEFT) + { + pContext->m_nBeginPos = length; + pContext->m_nCurrentPos = length; + endpos = 0; + } + else + { + pContext->m_nBeginPos = 0; + pContext->m_nCurrentPos = 0; + endpos = length; + } + + pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1); + pContext->m_captureindex[0] = 0; + pContext->m_capturestack.Push(0); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(-1); + + // match + if( ! m_builder.m_pTopElx->Match( pContext ) ) + return 0; + else + { + while( pContext->m_nCurrentPos != endpos ) + { + if( ! m_builder.m_pTopElx->MatchNext( pContext ) ) + return 0; + else + { + if( pContext->m_nLastBeginPos == pContext->m_nBeginPos && pContext->m_nBeginPos == pContext->m_nCurrentPos ) + return 0; + else + pContext->m_nLastBeginPos = pContext->m_nCurrentPos; + } + } + + // end pos + pContext->m_capturestack[2] = pContext->m_nCurrentPos; + + return MatchResult( pContext, m_builder.m_nMaxNumber ); + } +} + +template MatchResult CRegexpT :: Match(const CHART * tstring, int start, CContext * pContext) const +{ + return Match(tstring, CBufferRefT(tstring).GetSize(), start, pContext); +} + +template MatchResult CRegexpT :: Match(const CHART * tstring, int length, int start, CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + CContext context; + if(pContext == 0) pContext = &context; + + PrepareMatch(tstring, length, start, pContext); + + return Match( pContext ); +} + +template MatchResult CRegexpT :: Match(CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + int endpos, delta; + + if(m_builder.m_nFlags & RIGHTTOLEFT) + { + endpos = -1; + delta = -1; + } + else + { + endpos = pContext->m_pMatchStringLength + 1; + delta = 1; + } + + while(pContext->m_nCurrentPos != endpos) + { + pContext->m_captureindex.Restore(0); + pContext->m_stack .Restore(0); + pContext->m_capturestack.Restore(0); + + pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1); + pContext->m_captureindex[0] = 0; + pContext->m_capturestack.Push(0); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(-1); + + if( m_builder.m_pTopElx->Match( pContext ) ) + { + pContext->m_capturestack[2] = pContext->m_nCurrentPos; + + // zero width + if( pContext->m_capturestack[1] == pContext->m_nCurrentPos ) + { + pContext->m_nCurrentPos += delta; + } + + // save pos + pContext->m_nLastBeginPos = pContext->m_nBeginPos; + pContext->m_nBeginPos = pContext->m_nCurrentPos; + + // return + return MatchResult( pContext, m_builder.m_nMaxNumber ); + } + else + { + pContext->m_nCurrentPos += delta; + } + } + + return 0; +} + +template inline CContext * CRegexpT :: PrepareMatch(const CHART * tstring, int start, CContext * pContext) const +{ + return PrepareMatch(tstring, CBufferRefT(tstring).GetSize(), start, pContext); +} + +template CContext * CRegexpT :: PrepareMatch(const CHART * tstring, int length, int start, CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + if(pContext == 0) pContext = new CContext(); + + pContext->m_nParenZindex = 0; + pContext->m_nLastBeginPos = -1; + pContext->m_pMatchString = (void*)tstring; + pContext->m_pMatchStringLength = length; + pContext->m_nCursiveLimit = 100; + + if(start < 0) + { + if(m_builder.m_nFlags & RIGHTTOLEFT) + { + pContext->m_nBeginPos = length; + pContext->m_nCurrentPos = length; + } + else + { + pContext->m_nBeginPos = 0; + pContext->m_nCurrentPos = 0; + } + } + else + { + if(start > length) start = length + ((m_builder.m_nFlags & RIGHTTOLEFT)?0:1); + + pContext->m_nBeginPos = start; + pContext->m_nCurrentPos = start; + } + + return pContext; +} + +template inline int CRegexpT :: GetNamedGroupNumber(const CHART * group_name) const +{ + return m_builder.GetNamedNumber(group_name); +} + +template CHART * CRegexpT :: Replace(const CHART * tstring, const CHART * replaceto, int start, int ntimes, MatchResult * result, CContext * pContext) const +{ + int result_length = 0; + return Replace(tstring, CBufferRefT(tstring).GetSize(), replaceto, CBufferRefT(replaceto).GetSize(), result_length, start, ntimes, result, pContext); +} + +template CHART * CRegexpT :: Replace(const CHART * tstring, int string_length, const CHART * replaceto, int to_length, int & result_length, int start, int ntimes, MatchResult * remote_result, CContext * oContext) const +{ + if(m_builder.m_pTopElx == 0) return 0; + + // --- compile replace to --- + + CBufferT compiledto; + + static const CHART rtoptn[] = { RCHART('\\'), RCHART('$' ), RCHART('('), RCHART('?'), RCHART(':'), RCHART('[' ), RCHART('$' ), RCHART('&' ), RCHART('`' ), RCHART('\''), RCHART('+'), RCHART('_' ), RCHART('\\'), RCHART('d'), RCHART(']'), RCHART('|'), RCHART('\\'), RCHART('{'), RCHART('.'), RCHART('*'), RCHART('?'), RCHART('\\'), RCHART('}'), RCHART(')' ), RCHART('\0') }; + static CRegexpT rtoreg(rtoptn); + + MatchResult local_result(0), * result = remote_result ? remote_result : & local_result; + + // prepare + CContext * pContext = rtoreg.PrepareMatch(replaceto, to_length, -1, oContext); + int lastIndex = 0, nmatch = 0; + + while( ((*result) = rtoreg.Match(pContext)).IsMatched() ) + { + int delta = result->GetStart() - lastIndex; + if( delta > 0 ) + { + compiledto.Push(lastIndex); + compiledto.Push(delta); + } + + lastIndex = result->GetStart(); + delta = 2; + + switch(replaceto[lastIndex + 1]) + { + case RCHART('$'): + compiledto.Push(lastIndex); + compiledto.Push(1); + break; + + case RCHART('&'): + case RCHART('`'): + case RCHART('\''): + case RCHART('+'): + case RCHART('_'): + compiledto.Push(-1); + compiledto.Push((int)replaceto[lastIndex + 1]); + break; + + case RCHART('{'): + delta = result->GetEnd() - result->GetStart(); + nmatch = m_builder.GetNamedNumber(CBufferRefT (replaceto + (lastIndex + 2), delta - 3)); + + if(nmatch > 0 && nmatch <= m_builder.m_nMaxNumber) + { + compiledto.Push(-2); + compiledto.Push(nmatch); + } + else + { + compiledto.Push(lastIndex); + compiledto.Push(delta); + } + break; + + default: + nmatch = 0; + for(delta=1; delta<=3; delta++) + { + CHART ch = replaceto[lastIndex + delta]; + + if(ch < RCHART('0') || ch > RCHART('9')) + break; + + nmatch = nmatch * 10 + (ch - RCHART('0')); + } + + if(nmatch > m_builder.m_nMaxNumber) + { + while(nmatch > m_builder.m_nMaxNumber) + { + nmatch /= 10; + delta --; + } + + if(nmatch == 0) + { + delta = 1; + } + } + + if(delta == 1) + { + compiledto.Push(lastIndex); + compiledto.Push(1); + } + else + { + compiledto.Push(-2); + compiledto.Push(nmatch); + } + break; + } + + lastIndex += delta; + } + + if(lastIndex < to_length) + { + compiledto.Push(lastIndex); + compiledto.Push(to_length - lastIndex); + } + + int rightleft = m_builder.m_nFlags & RIGHTTOLEFT; + + int tb = rightleft ? compiledto.GetSize() - 2 : 0; + int te = rightleft ? -2 : compiledto.GetSize(); + int ts = rightleft ? -2 : 2; + + // --- compile complete --- + + int beginpos = rightleft ? string_length : 0; + int endpos = rightleft ? 0 : string_length; + + int toIndex0 = 0; + int toIndex1 = 0; + int i, ntime; + + CBufferT buffer; + + // prepare + pContext = PrepareMatch(tstring, string_length, start, pContext); + lastIndex = beginpos; + + // Match + for(ntime = 0; ntimes < 0 || ntime < ntimes; ntime ++) + { + (*result) = Match(pContext); + + if( ! result->IsMatched() ) + break; + + // before + if( rightleft ) + { + int distance = lastIndex - result->GetEnd(); + if( distance ) + { + buffer.Push(tstring + result->GetEnd()); + buffer.Push((const CHART *)distance); + + toIndex1 -= distance; + } + lastIndex = result->GetStart(); + } + else + { + int distance = result->GetStart() - lastIndex; + if( distance ) + { + buffer.Push(tstring + lastIndex); + buffer.Push((const CHART *)distance); + + toIndex1 += distance; + } + lastIndex = result->GetEnd(); + } + + toIndex0 = toIndex1; + + // middle + for(i=tb; i!=te; i+=ts) + { + int off = compiledto[i]; + int len = compiledto[i + 1]; + + const CHART * sub = replaceto + off; + + if( off == -1 ) + { + switch(RCHART(len)) + { + case RCHART('&'): + sub = tstring + result->GetStart(); + len = result->GetEnd() - result->GetStart(); + break; + + case RCHART('`'): + sub = tstring; + len = result->GetStart(); + break; + + case RCHART('\''): + sub = tstring + result->GetEnd(); + len = string_length - result->GetEnd(); + break; + + case RCHART('+'): + for(nmatch = result->MaxGroupNumber(); nmatch >= 0; nmatch --) + { + if(result->GetGroupStart(nmatch) >= 0) break; + } + sub = tstring + result->GetGroupStart(nmatch); + len = result->GetGroupEnd(nmatch) - result->GetGroupStart(nmatch); + break; + + case RCHART('_'): + sub = tstring; + len = string_length; + break; + } + } + else if( off == -2 ) + { + sub = tstring + result->GetGroupStart(len); + len = result->GetGroupEnd(len) - result->GetGroupStart(len); + } + + buffer.Push(sub); + buffer.Push((const CHART *)len); + + toIndex1 += rightleft ? (-len) : len; + } + } + + // after + if(rightleft) + { + if(endpos < lastIndex) + { + buffer.Push(tstring + endpos); + buffer.Push((const CHART *)(lastIndex - endpos)); + } + } + else + { + if(lastIndex < endpos) + { + buffer.Push(tstring + lastIndex); + buffer.Push((const CHART *)(endpos - lastIndex)); + } + } + + if(oContext == 0) ReleaseContext(pContext); + + // join string + result_length = 0; + for(i=0; i result_string; + result_string.Prepare(result_length); + result_string.Restore(0); + + if(rightleft) + { + for(i=buffer.GetSize()-2; i>=0; i-=2) + { + result_string.Append(buffer[i], (int)buffer[i+1]); + } + } + else + { + for(i=0; im_result.Append(result_length, 3); + result->m_result.Append(ntime); + + if(rightleft) + { + result->m_result.Append(result_length - toIndex1); + result->m_result.Append(result_length - toIndex0); + } + else + { + result->m_result.Append(toIndex0); + result->m_result.Append(toIndex1); + } + + return result_string.Detach(); +} + +template inline void CRegexpT :: ReleaseString(CHART * tstring) +{ + if(tstring != 0) free(tstring); +} + +template inline void CRegexpT :: ReleaseContext(CContext * pContext) +{ + if(pContext != 0) delete pContext; +} + +// +// All implementations +// +template CAlternativeElxT :: CAlternativeElxT() +{ +} + +template int CAlternativeElxT :: Match(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 1; + + // try all + for(int n = 0; n < m_elxlist.GetSize(); n++) + { + if(m_elxlist[n]->Match(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + } + + return 0; +} + +template int CAlternativeElxT :: MatchNext(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 0; + + int n = 0; + + // recall prev + pContext->m_stack.Pop(n); + + // prev + if(m_elxlist[n]->MatchNext(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + else + { + // try rest + for(n++; n < m_elxlist.GetSize(); n++) + { + if(m_elxlist[n]->Match(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + } + + return 0; + } +} + +// assertx.cpp: implementation of the CAssertElx class. +// +template CAssertElxT :: CAssertElxT(ElxInterface * pelx, int byes) +{ + m_pelx = pelx; + m_byes = byes; +} + +template int CAssertElxT :: Match(CContext * pContext) const +{ + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + int bsucc; + + // match + if( m_byes ) + bsucc = m_pelx->Match(pContext); + else + bsucc = ! m_pelx->Match(pContext); + + // status + pContext->m_stack.Restore(nsize); + pContext->m_nCurrentPos = nbegin; + + if( bsucc ) + pContext->m_stack.Push(ncsize); + else + pContext->m_capturestack.Restore(ncsize); + + return bsucc; +} + +template int CAssertElxT :: MatchNext(CContext * pContext) const +{ + int ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_capturestack.Restore(ncsize); + + return 0; +} + +// emptyelx.cpp: implementation of the CEmptyElx class. +// +template CEmptyElxT :: CEmptyElxT() +{ +} + +template int CEmptyElxT :: Match(CContext *) const +{ + return 1; +} + +template int CEmptyElxT :: MatchNext(CContext *) const +{ + return 0; +} + +// globalx.cpp: implementation of the CGlobalElx class. +// +template CGlobalElxT ::CGlobalElxT() +{ +} + +template int CGlobalElxT :: Match(CContext * pContext) const +{ + return pContext->m_nCurrentPos == pContext->m_nBeginPos; +} + +template int CGlobalElxT :: MatchNext(CContext *) const +{ + return 0; +} + +// greedelx.cpp: implementation of the CGreedyElx class. +// +template CGreedyElxT :: CGreedyElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT (pelx, nmin) +{ + m_nvart = nmax - nmin; +} + +template int CGreedyElxT :: Match(CContext * pContext) const +{ + if( ! CRepeatElxT :: MatchFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CGreedyElxT :: MatchNext(CContext * pContext) const +{ + if( MatchNextVart(pContext) ) + return 1; + + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CGreedyElxT :: MatchVart(CContext * pContext) const +{ + int n = 0; + int nbegin00 = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + + while(n < m_nvart && CRepeatElx::MatchForward(pContext)) + { + n ++; + } + + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(nsize); + pContext->m_stack.Push(pContext->m_nCurrentPos); + pContext->m_stack.Push(1); + pContext->m_stack.Push(nbegin00); + pContext->m_stack.Push(n); + + return 1; +} + +template int CGreedyElxT :: MatchNextVart(CContext * pContext) const +{ + int n, nbegin00, nsize, ncsize; + CSortedBufferT nbegin99; + pContext->m_stack.Pop(n); + pContext->m_stack.Pop(nbegin00); + pContext->m_stack.Pop(nbegin99); + pContext->m_stack.Pop(nsize); + pContext->m_stack.Pop(ncsize); + + if(n == 0) return 0; + + int n0 = n; + + if( ! CRepeatElxT::m_pelx->MatchNext(pContext) ) + { + n --; + } + + // not to re-match + else if(pContext->m_nCurrentPos == nbegin00) + { + pContext->m_stack.Restore(nsize); + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin00; + + return 0; + } + + // fix 2012-10-26, thanks to chenlx01@sohu.com + else + { + CContextShot shot(pContext); + + while(n < m_nvart && CRepeatElx::MatchForward(pContext)) + { + n ++; + } + + if(nbegin99.Find(pContext->m_nCurrentPos) >= 0) + { + shot.Restore(pContext); + n = n0; + } + else + { + nbegin99.Add(pContext->m_nCurrentPos); + } + } + + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(nsize); + pContext->m_stack.Push(nbegin99); + pContext->m_stack.Push(nbegin00); + pContext->m_stack.Push(n); + + return 1; +} + +// indepelx.cpp: implementation of the CIndependentElx class. +// +template CIndependentElxT :: CIndependentElxT(ElxInterface * pelx) +{ + m_pelx = pelx; +} + +template int CIndependentElxT :: Match(CContext * pContext) const +{ + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + + // match + int bsucc = m_pelx->Match(pContext); + + // status + pContext->m_stack.Restore(nsize); + + if( bsucc ) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(ncsize); + } + + return bsucc; +} + +template int CIndependentElxT :: MatchNext(CContext * pContext) const +{ + int nbegin = 0, ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_stack.Pop(nbegin); + + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin; + + return 0; +} + +// listelx.cpp: implementation of the CListElx class. +// +template CListElxT :: CListElxT(int brightleft) +{ + m_brightleft = brightleft; +} + +template int CListElxT :: Match(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 1; + + // prepare + int bol = m_brightleft ? m_elxlist.GetSize() : -1; + int stp = m_brightleft ? -1 : 1; + int eol = m_brightleft ? -1 : m_elxlist.GetSize(); + + // from first + int n = bol + stp; + + // match all + while(n != eol) + { + if(m_elxlist[n]->Match(pContext)) + { + n += stp; + } + else + { + n -= stp; + + while(n != bol && ! m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if(n != bol) + n += stp; + else + return 0; + } + } + + return 1; +} + +template int CListElxT :: MatchNext(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 0; + + // prepare + int bol = m_brightleft ? m_elxlist.GetSize() : -1; + int stp = m_brightleft ? -1 : 1; + int eol = m_brightleft ? -1 : m_elxlist.GetSize(); + + // from last + int n = eol - stp; + + while(n != bol && ! m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if(n != bol) + n += stp; + else + return 0; + + // match rest + while(n != eol) + { + if(m_elxlist[n]->Match(pContext)) + { + n += stp; + } + else + { + n -= stp; + + while(n != bol && ! m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if(n != bol) + n += stp; + else + return 0; + } + } + + return 1; +} + +// mresult.cpp: implementation of the MatchResult class. +// +template MatchResultT :: MatchResultT(CContext * pContext, int nMaxNumber) +{ + if(pContext != 0) + { + m_result.Prepare(nMaxNumber * 2 + 3, -1); + + // matched + m_result[0] = 1; + m_result[1] = nMaxNumber; + + for(int n = 0; n <= nMaxNumber; n++) + { + int index = pContext->m_captureindex[n]; + //if( index < 0 ) continue; + if( ! CBracketElxT::CheckCaptureIndex(index, pContext, n) ) continue; + + // check enclosed + int pos1 = pContext->m_capturestack[index + 1]; + int pos2 = pContext->m_capturestack[index + 2]; + + // info + m_result[n*2 + 2] = pos1 < pos2 ? pos1 : pos2; + m_result[n*2 + 3] = pos1 < pos2 ? pos2 : pos1; + } + } +} + +template inline int MatchResultT :: IsMatched() const +{ + return m_result.At(0, 0); +} + +template inline int MatchResultT :: MaxGroupNumber() const +{ + return m_result.At(1, 0); +} + +template inline int MatchResultT :: GetStart() const +{ + return m_result.At(2, -1); +} + +template inline int MatchResultT :: GetEnd() const +{ + return m_result.At(3, -1); +} + +template inline int MatchResultT :: GetGroupStart(int nGroupNumber) const +{ + return m_result.At(2 + nGroupNumber * 2, -1); +} + +template inline int MatchResultT :: GetGroupEnd(int nGroupNumber) const +{ + return m_result.At(2 + nGroupNumber * 2 + 1, -1); +} + +template MatchResultT & MatchResultT :: operator = (const MatchResultT & result) +{ + m_result.Restore(0); + if(result.m_result.GetSize() > 0) m_result.Append(result.m_result.GetBuffer(), result.m_result.GetSize()); + + return *this; +} + +// posselx.cpp: implementation of the CPossessiveElx class. +// +template CPossessiveElxT :: CPossessiveElxT(ElxInterface * pelx, int nmin, int nmax) : CGreedyElxT (pelx, nmin, nmax) +{ +} + +template int CPossessiveElxT :: Match(CContext * pContext) const +{ + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + int bsucc = 1; + + // match + if( ! CRepeatElxT :: MatchFixed(pContext) ) + { + bsucc = 0; + } + else + { + while( ! CGreedyElxT :: MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + { + bsucc = 0; + break; + } + } + } + + // status + pContext->m_stack.Restore(nsize); + + if( bsucc ) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(ncsize); + } + + return bsucc; +} + +template int CPossessiveElxT :: MatchNext(CContext * pContext) const +{ + int nbegin = 0, ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_stack.Pop(nbegin); + + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin; + + return 0; +} + +// reluctx.cpp: implementation of the CReluctantElx class. +// +template CReluctantElxT :: CReluctantElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT (pelx, nmin) +{ + m_nvart = nmax - nmin; +} + +template int CReluctantElxT :: Match(CContext * pContext) const +{ + if( ! CRepeatElxT :: MatchFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CReluctantElxT :: MatchNext(CContext * pContext) const +{ + if( MatchNextVart(pContext) ) + return 1; + + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CReluctantElxT :: MatchVart(CContext * pContext) const +{ + pContext->m_stack.Push(0); + + return 1; +} + +template int CReluctantElxT :: MatchNextVart(CContext * pContext) const +{ + int n = 0, nbegin = pContext->m_nCurrentPos; + + pContext->m_stack.Pop(n); + + if(n < m_nvart && CRepeatElxT :: m_pelx->Match(pContext)) + { + while(pContext->m_nCurrentPos == nbegin) + { + if( ! CRepeatElxT :: m_pelx->MatchNext(pContext) ) break; + } + + if(pContext->m_nCurrentPos != nbegin) + { + n ++; + + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(n); + + return 1; + } + } + + while(n > 0) + { + pContext->m_stack.Pop(nbegin); + + while( CRepeatElxT :: m_pelx->MatchNext(pContext) ) + { + if(pContext->m_nCurrentPos != nbegin) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(n); + + return 1; + } + } + + n --; + } + + return 0; +} + +// repeatx.cpp: implementation of the CRepeatElx class. +// +template CRepeatElxT :: CRepeatElxT(ElxInterface * pelx, int ntimes) +{ + m_pelx = pelx; + m_nfixed = ntimes; +} + +template int CRepeatElxT :: Match(CContext * pContext) const +{ + return MatchFixed(pContext); +} + +template int CRepeatElxT :: MatchNext(CContext * pContext) const +{ + return MatchNextFixed(pContext); +} + +template int CRepeatElxT :: MatchFixed(CContext * pContext) const +{ + if(m_nfixed == 0) + return 1; + + int n = 0; + + while(n < m_nfixed) + { + if(m_pelx->Match(pContext)) + { + n ++; + } + else + { + n --; + + while(n >= 0 && ! m_pelx->MatchNext(pContext)) + n --; + + if(n >= 0) + n ++; + else + return 0; + } + } + + return 1; +} + +template int CRepeatElxT :: MatchNextFixed(CContext * pContext) const +{ + if(m_nfixed == 0) + return 0; + + // from last + int n = m_nfixed - 1; + + while(n >= 0 && ! m_pelx->MatchNext(pContext)) + n --; + + if(n >= 0) + n ++; + else + return 0; + + // match rest + while(n < m_nfixed) + { + if(m_pelx->Match(pContext)) + { + n ++; + } + else + { + n --; + + while(n >= 0 && ! m_pelx->MatchNext(pContext)) + n --; + + if(n >= 0) + n ++; + else + return 0; + } + } + + return 1; +} + +// Regexp +typedef CRegexpT CRegexpA; +typedef CRegexpT CRegexpW; + +#if defined(_UNICODE) || defined(UNICODE) + typedef CRegexpW CRegexp; +#else + typedef CRegexpA CRegexp; +#endif + +#endif//__DEELX_REGEXP__H__ diff --git a/scintilla/deelx/doc/orig_src/deelx13.h b/scintilla/deelx/doc/orig_src/deelx13.h new file mode 100644 index 000000000..8b1a6ed9e --- /dev/null +++ b/scintilla/deelx/doc/orig_src/deelx13.h @@ -0,0 +1,4804 @@ +// deelx.h +// +// DEELX Regular Expression Engine (v1.3) +// +// Copyright 2006 ~ 2013 (c) RegExLab.com +// All Rights Reserved. +// +// http://www.regexlab.com/deelx/ +// +// Author: Ê·ÊÙΰ (sswater shi) +// sswater@gmail.com +// +// $Revision$ +// + +#ifndef __DEELX_REGEXP__H__ +#define __DEELX_REGEXP__H__ + +#include +#include +#include +#include +#include + +extern "C" { + typedef int (*POSIX_FUNC)(int); + int _isblank(int c); +} + +// +// Data Reference +// +template class CBufferRefT +{ +public: + CBufferRefT(const ELT * pcsz, int length); + CBufferRefT(const ELT * pcsz); + +public: + int nCompare (const ELT * pcsz) const; + int nCompareNoCase(const ELT * pcsz) const; + int Compare (const ELT * pcsz) const; + int CompareNoCase(const ELT * pcsz) const; + int Compare (const CBufferRefT &) const; + int CompareNoCase(const CBufferRefT &) const; + + ELT At (int nIndex, ELT def = 0) const; + ELT operator [] (int nIndex) const; + + const ELT * GetBuffer() const; + int GetSize() const; + +public: + virtual ~CBufferRefT(); + +// Content +protected: + ELT * m_pBuffer; + int m_nSize; +}; + +// +// Implemenation +// +template CBufferRefT :: CBufferRefT(const ELT * pcsz, int length) +{ + m_pBuffer = (ELT *)pcsz; + m_nSize = length; +} + +template CBufferRefT :: CBufferRefT(const ELT * pcsz) +{ + m_pBuffer = (ELT *)pcsz; + m_nSize = 0; + + if(pcsz != 0) while(m_pBuffer[m_nSize] != 0) m_nSize ++; +} + +template int CBufferRefT :: nCompare(const ELT * pcsz) const +{ + for(int i=0; i int CBufferRefT :: nCompareNoCase(const ELT * pcsz) const +{ + for(int i=0; i inline int CBufferRefT :: Compare(const ELT * pcsz) const +{ + return nCompare(pcsz) ? 1 : (int)pcsz[m_nSize]; +} + +template inline int CBufferRefT :: CompareNoCase(const ELT * pcsz) const +{ + return nCompareNoCase(pcsz) ? 1 : (int)pcsz[m_nSize]; +} + +template inline int CBufferRefT :: Compare(const CBufferRefT & cref) const +{ + return m_nSize == cref.m_nSize ? nCompare(cref.GetBuffer()) : 1; +} + +template inline int CBufferRefT :: CompareNoCase(const CBufferRefT & cref) const +{ + return m_nSize == cref.m_nSize ? nCompareNoCase(cref.GetBuffer()) : 1; +} + +template inline ELT CBufferRefT :: At(int nIndex, ELT def) const +{ + return nIndex >= m_nSize ? def : m_pBuffer[nIndex]; +} + +template inline ELT CBufferRefT :: operator [] (int nIndex) const +{ + return nIndex >= m_nSize ? 0 : m_pBuffer[nIndex]; +} + +template const ELT * CBufferRefT :: GetBuffer() const +{ + static const ELT _def[] = {0}; return m_pBuffer ? m_pBuffer : _def; +} + +template inline int CBufferRefT :: GetSize() const +{ + return m_nSize; +} + +template CBufferRefT :: ~CBufferRefT() +{ +} + +// +// Data Buffer +// +template class CBufferT : public CBufferRefT +{ +public: + CBufferT(const ELT * pcsz, int length); + CBufferT(const ELT * pcsz); + CBufferT(); + +public: + ELT & operator [] (int nIndex); + const ELT & operator [] (int nIndex) const; + void Append(const ELT * pcsz, int length, int eol = 0); + void Append(ELT el, int eol = 0); + +public: + void Push(ELT el); + void Push(const CBufferRefT & buf); + int Pop (ELT & el); + int Pop (CBufferT & buf); + int Peek(ELT & el) const; + +public: + const ELT * GetBuffer() const; + ELT * GetBuffer(); + ELT * Detach(); + void Release(); + void Prepare(int index, int fill = 0); + void Restore(int size); + + ELT * PrepareInsert(int nPos, int nSize) + { + int nOldSize = CBufferRefT::m_nSize; + Restore(nPos > CBufferRefT::m_nSize ? nPos : CBufferRefT::m_nSize + nSize); + + if( nPos < nOldSize ) + { + ELT * from = CBufferRefT::m_pBuffer + nPos, * to = CBufferRefT::m_pBuffer + nPos + nSize; + memmove(to, from, sizeof(ELT) * (nOldSize - nPos)); + } + + return CBufferRefT::m_pBuffer + nPos; + } + + void Insert(int nIndex, const ELT & rT) + { + Insert(nIndex, &rT, 1); + } + + void Insert(int nIndex, const ELT * pT, int nSize) + { + memcpy(PrepareInsert(nIndex, nSize), pT, sizeof(ELT) * nSize); + } + + void Remove(int nIndex) + { + Remove(nIndex, 1); + } + + void Remove(int nIndex, int nSize) + { + if( nIndex < CBufferRefT :: m_nSize ) + { + if( nIndex + nSize >= CBufferRefT :: m_nSize ) + { + Restore(nIndex); + } + else + { + memmove(CBufferRefT :: m_pBuffer + nIndex, CBufferRefT :: m_pBuffer + nIndex + nSize, sizeof(ELT) * (CBufferRefT :: m_nSize - nIndex - nSize)); + Restore(CBufferRefT :: m_nSize - nSize); + } + } + } + + void SetMaxLength(int nSize) + { + if( nSize > m_nMaxLength ) + { + if( m_nMaxLength < 8 ) + m_nMaxLength = 8; + + if( nSize > m_nMaxLength ) + m_nMaxLength *= 2; + + if( nSize > m_nMaxLength ) + { + m_nMaxLength = nSize + 11; + m_nMaxLength -= m_nMaxLength & 0x07; + } + + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT :: m_pBuffer, sizeof(ELT) * m_nMaxLength); + } + } + +public: + virtual ~CBufferT(); + +// Content +protected: + int m_nMaxLength; +}; + +// +// Implemenation +// +template CBufferT :: CBufferT(const ELT * pcsz, int length) : CBufferRefT (0, length) +{ + m_nMaxLength = CBufferRefT :: m_nSize + 1; + + CBufferRefT :: m_pBuffer = (ELT *) malloc(sizeof(ELT) * m_nMaxLength); + memcpy(CBufferRefT::m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT :: m_nSize); + CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize] = 0; +} + +template CBufferT :: CBufferT(const ELT * pcsz) : CBufferRefT (pcsz) +{ + m_nMaxLength = CBufferRefT :: m_nSize + 1; + + CBufferRefT :: m_pBuffer = (ELT *) malloc(sizeof(ELT) * m_nMaxLength); + memcpy(CBufferRefT::m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT :: m_nSize); + CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize] = 0; +} + +template CBufferT :: CBufferT() : CBufferRefT (0, 0) +{ + m_nMaxLength = 0; + CBufferRefT::m_pBuffer = 0; +} + +template inline ELT & CBufferT :: operator [] (int nIndex) +{ + return CBufferRefT::m_pBuffer[nIndex]; +} + +template inline const ELT & CBufferT :: operator [] (int nIndex) const +{ + return CBufferRefT::m_pBuffer[nIndex]; +} + +template void CBufferT :: Append(const ELT * pcsz, int length, int eol) +{ + int nNewLength = m_nMaxLength; + + // Check length + if(nNewLength < 8) + nNewLength = 8; + + if(CBufferRefT :: m_nSize + length + eol > nNewLength) + nNewLength *= 2; + + if(CBufferRefT :: m_nSize + length + eol > nNewLength) + { + nNewLength = CBufferRefT :: m_nSize + length + eol + 11; + nNewLength -= nNewLength % 8; + } + + // Realloc + if(nNewLength > m_nMaxLength) + { + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // Append + memcpy(CBufferRefT::m_pBuffer + CBufferRefT :: m_nSize, pcsz, sizeof(ELT) * length); + CBufferRefT :: m_nSize += length; + + if(eol > 0) CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize] = 0; +} + +template inline void CBufferT :: Append(ELT el, int eol) +{ + Append(&el, 1, eol); +} + +template void CBufferT :: Push(ELT el) +{ + // Realloc + if(CBufferRefT :: m_nSize >= m_nMaxLength) + { + int nNewLength = m_nMaxLength * 2; + if( nNewLength < 8 ) nNewLength = 8; + + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // Append + CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize++] = el; +} + +template void CBufferT :: Push(const CBufferRefT & buf) +{ + for(int i=0; i inline int CBufferT :: Pop(ELT & el) +{ + if(CBufferRefT :: m_nSize > 0) + { + el = CBufferRefT::m_pBuffer[--CBufferRefT :: m_nSize]; + return 1; + } + else + { + return 0; + } +} + +template int CBufferT :: Pop (CBufferT & buf) +{ + int size, res = 1; + res = res && Pop(*(ELT*)&size); + buf.Restore(size); + + for(int i=size-1; i>=0; i--) + { + res = res && Pop(buf[i]); + } + + return res; +} + +template inline int CBufferT :: Peek(ELT & el) const +{ + if(CBufferRefT :: m_nSize > 0) + { + el = CBufferRefT::m_pBuffer[CBufferRefT :: m_nSize - 1]; + return 1; + } + else + { + return 0; + } +} + +template const ELT * CBufferT :: GetBuffer() const +{ + static const ELT _def[] = {0}; return CBufferRefT::m_pBuffer ? CBufferRefT::m_pBuffer : _def; +} + +template ELT * CBufferT :: GetBuffer() +{ + static const ELT _def[] = {0}; return CBufferRefT::m_pBuffer ? CBufferRefT::m_pBuffer : (ELT *)_def; +} + +template ELT * CBufferT :: Detach() +{ + ELT * pBuffer = CBufferRefT::m_pBuffer; + + CBufferRefT :: m_pBuffer = 0; + CBufferRefT :: m_nSize = m_nMaxLength = 0; + + return pBuffer; +} + +template void CBufferT :: Release() +{ + ELT * pBuffer = Detach(); + + if(pBuffer != 0) free(pBuffer); +} + +template void CBufferT :: Prepare(int index, int fill) +{ + int nNewSize = index + 1; + + // Realloc + if(nNewSize > m_nMaxLength) + { + int nNewLength = m_nMaxLength; + + if( nNewLength < 8 ) + nNewLength = 8; + + if( nNewSize > nNewLength ) + nNewLength *= 2; + + if( nNewSize > nNewLength ) + { + nNewLength = nNewSize + 11; + nNewLength -= nNewLength % 8; + } + + CBufferRefT :: m_pBuffer = (ELT *) realloc(CBufferRefT::m_pBuffer, sizeof(ELT) * nNewLength); + m_nMaxLength = nNewLength; + } + + // size + if( CBufferRefT :: m_nSize < nNewSize ) + { + memset(CBufferRefT::m_pBuffer + CBufferRefT :: m_nSize, fill, sizeof(ELT) * (nNewSize - CBufferRefT :: m_nSize)); + CBufferRefT :: m_nSize = nNewSize; + } +} + +template inline void CBufferT :: Restore(int size) +{ + SetMaxLength(size); + CBufferRefT :: m_nSize = size; +} + +template CBufferT :: ~CBufferT() +{ + if(CBufferRefT::m_pBuffer != 0) free(CBufferRefT::m_pBuffer); +} + +template class CSortedBufferT : public CBufferT +{ +public: + CSortedBufferT(int reverse = 0); + CSortedBufferT(int(*)(const void *, const void *)); + +public: + void Add(const T & rT); + void Add(const T * pT, int nSize); + int Remove(const T & rT); + void RemoveAll(); + + void SortFreeze() { m_bSortFreezed = 1; } + void SortUnFreeze(); + +public: + int Find(const T & rT, int(* compare)(const void *, const void *) = 0) { return FindAs(*(T*)&rT, compare); } + int FindAs(const T & rT, int(*)(const void *, const void *) = 0); + int GetSize() const { return CBufferRefT::m_nSize; } + T & operator [] (int nIndex) { return CBufferT :: operator [] (nIndex); } + +protected: + int (* m_fncompare)(const void *, const void *); + static int compareT(const void *, const void *); + static int compareReverseT(const void *, const void *); + + int m_bSortFreezed; +}; + +template CSortedBufferT :: CSortedBufferT(int reverse) +{ + m_fncompare = reverse ? compareReverseT : compareT; + m_bSortFreezed = 0; +} + +template CSortedBufferT :: CSortedBufferT(int (* compare)(const void *, const void *)) +{ + m_fncompare = compare; + m_bSortFreezed = 0; +} + +template void CSortedBufferT :: Add(const T & rT) +{ + if(m_bSortFreezed != 0) + { + CBufferT :: Append(rT); + return; + } + + int a = 0, b = CBufferRefT::m_nSize - 1, c = CBufferRefT::m_nSize / 2; + + while(a <= b) + { + int r = m_fncompare(&rT, &CBufferRefT::m_pBuffer[c]); + + if ( r < 0 ) b = c - 1; + else if( r > 0 ) a = c + 1; + else break; + + c = (a + b + 1) / 2; + } + + CBufferT :: Insert(c, rT); +} + +template void CSortedBufferT :: Add(const T * pT, int nSize) +{ + CBufferT :: Append(pT, nSize); + + if(m_bSortFreezed == 0) + { + qsort(CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), m_fncompare); + } +} + +template int CSortedBufferT :: FindAs(const T & rT, int(* compare)(const void *, const void *)) +{ + const T * pT = (const T *)bsearch(&rT, CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), compare == 0 ? m_fncompare : compare); + + if( pT != NULL ) + return pT - CBufferRefT::m_pBuffer; + else + return -1; +} + +template int CSortedBufferT :: Remove(const T & rT) +{ + int pos = Find(rT); + if( pos >= 0 ) CBufferT :: Remove(pos); + return pos; +} + +template inline void CSortedBufferT :: RemoveAll() +{ + CBufferT::Restore(0); +} + +template void CSortedBufferT :: SortUnFreeze() +{ + if(m_bSortFreezed != 0) + { + m_bSortFreezed = 0; + qsort(CBufferRefT::m_pBuffer, CBufferRefT::m_nSize, sizeof(T), m_fncompare); + } +} + +template int CSortedBufferT :: compareT(const void * elem1, const void * elem2) +{ + if( *(const T *)elem1 == *(const T *)elem2 ) + return 0; + else if( *(const T *)elem1 < *(const T *)elem2 ) + return -1; + else + return 1; +} + +template int CSortedBufferT :: compareReverseT(const void * elem1, const void * elem2) +{ + if( *(const T *)elem1 == *(const T *)elem2 ) + return 0; + else if( *(const T *)elem1 > *(const T *)elem2 ) + return -1; + else + return 1; +} + +// +// Context +// +class CContext +{ +public: + CBufferT m_stack; + CBufferT m_capturestack, m_captureindex; + +public: + int m_nCurrentPos; + int m_nBeginPos; + int m_nLastBeginPos; + int m_nParenZindex; + int m_nCursiveLimit; + + void * m_pMatchString; + int m_pMatchStringLength; +}; + +class CContextShot +{ +public: + CContextShot(CContext * pContext) + { + m_nCurrentPos = pContext->m_nCurrentPos; + nsize = pContext->m_stack.GetSize(); + ncsize = pContext->m_capturestack.GetSize(); + } + + void Restore(CContext * pContext) + { + pContext->m_stack.Restore(nsize); + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = m_nCurrentPos; + } + +public: + int m_nCurrentPos; + int nsize ; + int ncsize; +}; + +// +// Interface +// +class ElxInterface +{ +public: + virtual int Match (CContext * pContext) const = 0; + virtual int MatchNext(CContext * pContext) const = 0; + +public: + virtual ~ElxInterface() {}; +}; + +// +// Alternative +// +template class CAlternativeElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CAlternativeElxT(); + +public: + CBufferT m_elxlist; +}; + +typedef CAlternativeElxT <0> CAlternativeElx; + +// +// Assert +// +template class CAssertElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CAssertElxT(ElxInterface * pelx, int byes = 1); + +public: + ElxInterface * m_pelx; + int m_byes; +}; + +typedef CAssertElxT <0> CAssertElx; + +// +// Back reference elx +// +template class CBackrefElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBackrefElxT(int nnumber, int brightleft, int bignorecase); + +public: + int m_nnumber; + int m_brightleft; + int m_bignorecase; + + CBufferT m_szNamed; +}; + +// +// Implementation +// +template CBackrefElxT :: CBackrefElxT(int nnumber, int brightleft, int bignorecase) +{ + m_nnumber = nnumber; + m_brightleft = brightleft; + m_bignorecase = bignorecase; +} + +template int CBackrefElxT :: Match(CContext * pContext) const +{ + // check number, for named + if( m_nnumber < 0 || m_nnumber >= pContext->m_captureindex.GetSize() ) return 0; + + int index = pContext->m_captureindex[m_nnumber]; + if( index < 0 ) return 0; + + // check enclosed + int pos1 = pContext->m_capturestack[index + 1]; + int pos2 = pContext->m_capturestack[index + 2]; + + if( pos2 < 0 ) pos2 = pContext->m_nCurrentPos; + + // info + int lpos = pos1 < pos2 ? pos1 : pos2; + int rpos = pos1 < pos2 ? pos2 : pos1; + int slen = rpos - lpos; + + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + int npos = pContext->m_nCurrentPos; + int tlen = pContext->m_pMatchStringLength; + + // compare + int bsucc; + CBufferRefT refstr(pcsz + lpos, slen); + + if( m_brightleft ) + { + if(npos < slen) + return 0; + + if(m_bignorecase) + bsucc = ! refstr.nCompareNoCase(pcsz + (npos - slen)); + else + bsucc = ! refstr.nCompare (pcsz + (npos - slen)); + + if( bsucc ) + { + pContext->m_stack.Push(npos); + pContext->m_nCurrentPos -= slen; + } + } + else + { + if(npos + slen > tlen) + return 0; + + if(m_bignorecase) + bsucc = ! refstr.nCompareNoCase(pcsz + npos); + else + bsucc = ! refstr.nCompare (pcsz + npos); + + if( bsucc ) + { + pContext->m_stack.Push(npos); + pContext->m_nCurrentPos += slen; + } + } + + return bsucc; +} + +template int CBackrefElxT :: MatchNext(CContext * pContext) const +{ + int npos = 0; + + pContext->m_stack.Pop(npos); + pContext->m_nCurrentPos = npos; + + return 0; +} + +// RCHART +#ifndef RCHART + #define RCHART(ch) ((CHART)ch) +#endif + +// BOUNDARY_TYPE +enum BOUNDARY_TYPE +{ + BOUNDARY_FILE_BEGIN, // begin of whole text + BOUNDARY_FILE_END , // end of whole text + BOUNDARY_FILE_END_N, // end of whole text, or before newline at the end + BOUNDARY_LINE_BEGIN, // begin of line + BOUNDARY_LINE_END , // end of line + BOUNDARY_WORD_BEGIN, // begin of word + BOUNDARY_WORD_END , // end of word + BOUNDARY_WORD_EDGE +}; + +// +// Boundary Elx +// +template class CBoundaryElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBoundaryElxT(int ntype, int byes = 1); + +protected: + static int IsWordChar(CHART ch); + +public: + int m_ntype; + int m_byes; +}; + +// +// Implementation +// +template CBoundaryElxT :: CBoundaryElxT(int ntype, int byes) +{ + m_ntype = ntype; + m_byes = byes; +} + +template int CBoundaryElxT :: Match(CContext * pContext) const +{ + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + int npos = pContext->m_nCurrentPos; + int tlen = pContext->m_pMatchStringLength; + + CHART chL = npos > 0 ? pcsz[npos - 1] : 0; + CHART chR = npos < tlen ? pcsz[npos ] : 0; + + int bsucc = 0; + + switch(m_ntype) + { + case BOUNDARY_FILE_BEGIN: + bsucc = (npos <= 0); + break; + + case BOUNDARY_FILE_END: + bsucc = (npos >= tlen); + break; + + case BOUNDARY_FILE_END_N: + bsucc = (npos >= tlen) || (pcsz[tlen-1] == RCHART('\n') && (npos == tlen-1 || (pcsz[tlen-2] == RCHART('\r') && npos == tlen-2))); + break; + + case BOUNDARY_LINE_BEGIN: + bsucc = (npos <= 0 ) || (chL == RCHART('\n')) || ((chL == RCHART('\r')) && (chR != RCHART('\n'))); + break; + + case BOUNDARY_LINE_END: + bsucc = (npos >= tlen) || (chR == RCHART('\r')) || ((chR == RCHART('\n')) && (chL != RCHART('\r'))); + break; + + case BOUNDARY_WORD_BEGIN: + bsucc = ! IsWordChar(chL) && IsWordChar(chR); + break; + + case BOUNDARY_WORD_END: + bsucc = IsWordChar(chL) && ! IsWordChar(chR); + break; + + case BOUNDARY_WORD_EDGE: + bsucc = IsWordChar(chL) ? ! IsWordChar(chR) : IsWordChar(chR); + break; + } + + return m_byes ? bsucc : ! bsucc; +} + +template int CBoundaryElxT :: MatchNext(CContext *) const +{ + return 0; +} + +template inline int CBoundaryElxT :: IsWordChar(CHART ch) +{ + return (ch >= RCHART('A') && ch <= RCHART('Z')) || (ch >= RCHART('a') && ch <= RCHART('z')) || (ch >= RCHART('0') && ch <= RCHART('9')) || (ch == RCHART('_')); +} + +// +// Bracket +// +template class CBracketElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CBracketElxT(int nnumber, int bright); + static int CheckCaptureIndex(int & index, CContext * pContext, int number); + +public: + int m_nnumber; + int m_bright; + int m_balancing; + + CBufferT m_szNamed; + CBufferT m_szBalancing; +}; + +template CBracketElxT :: CBracketElxT(int nnumber, int bright) +{ + m_nnumber = nnumber; + m_bright = bright; + m_balancing = -1; +} + +template inline int CBracketElxT :: CheckCaptureIndex(int & index, CContext * pContext, int number) +{ + if( index >= pContext->m_capturestack.GetSize() ) + index = pContext->m_capturestack.GetSize() - 4; + + while(index >= 0) + { + if(pContext->m_capturestack[index] == number) + { + return 1; + } + + index -= 4; + } + + + return 0; +} + +// +// capturestack[index+0] => Group number +// capturestack[index+1] => Capture start pos +// capturestack[index+2] => Capture end pos +// capturestack[index+3] => Capture enclose z-index, zindex<0 means inner group with same name +// +template int CBracketElxT :: Match(CContext * pContext) const +{ + // check, for named + if(m_nnumber < 0) return 0; + + if( ! m_bright ) + { + pContext->m_captureindex.Prepare(m_nnumber, -1); + int index = pContext->m_captureindex[m_nnumber]; + + // check + if(CheckCaptureIndex(index, pContext, m_nnumber) && pContext->m_capturestack[index+2] < 0) + { + pContext->m_capturestack[index+3] --; + return 1; + } + + // balancing left + if(m_balancing >= 0) + { + int balancing_index = pContext->m_captureindex[m_balancing]; + if( ! CheckCaptureIndex(balancing_index, pContext, m_balancing) || + pContext->m_capturestack[balancing_index+2] < 0 ) + { + return 0; + } + } + + // save + pContext->m_captureindex[m_nnumber] = pContext->m_capturestack.GetSize(); + + pContext->m_capturestack.Push(m_nnumber); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push( 0); // z-index + } + else + { + // check + int index = pContext->m_captureindex[m_nnumber]; + + if(CheckCaptureIndex(index, pContext, m_nnumber)) + { + if(pContext->m_capturestack[index + 3] < 0) // check inner group with same name + { + pContext->m_capturestack[index + 3] ++; + return 1; + } + + // balancing right + int balancing_index = -1; + if(m_balancing >= 0) + { + balancing_index = pContext->m_captureindex[m_balancing]; + if( ! CheckCaptureIndex(balancing_index, pContext, m_balancing) ) + { + // TODO ERROR + return 0; + } + } + + // save + pContext->m_capturestack[index + 2] = pContext->m_nCurrentPos; + pContext->m_capturestack[index + 3] = pContext->m_nParenZindex ++; + + // balancing right + if(m_balancing >= 0) + { + // backup index + pContext->m_stack.Push(balancing_index); + + if(balancing_index >= 0) + { + pContext->m_capturestack[index+2] = pContext->m_capturestack[index+1]; + pContext->m_capturestack[index+1] = pContext->m_capturestack[balancing_index+2]; + + // destopy capture + pContext->m_capturestack[balancing_index] = -1; + balancing_index -= 4; + CheckCaptureIndex(balancing_index, pContext, m_balancing); + pContext->m_captureindex[m_balancing] = balancing_index; + } + } + } + } + + return 1; +} + +template int CBracketElxT :: MatchNext(CContext * pContext) const +{ + int index = pContext->m_captureindex[m_nnumber]; + if( ! CheckCaptureIndex(index, pContext, m_nnumber) ) + { + return 0; + } + + if( ! m_bright ) + { + if(pContext->m_capturestack[index + 3] < 0) + { + pContext->m_capturestack[index + 3] ++; + return 0; + } + + pContext->m_capturestack.Restore(pContext->m_capturestack.GetSize() - 4); + + // to find + CheckCaptureIndex(index, pContext, m_nnumber); + + // new index + pContext->m_captureindex[m_nnumber] = index; + } + else + { + if( pContext->m_capturestack[index + 2] >= 0 ) + { + // balancing right + if(m_balancing >= 0) + { + int balancing_index = -1; + pContext->m_stack.Pop(balancing_index); + + if(balancing_index >= 0) + { + pContext->m_capturestack[balancing_index] = m_balancing; + pContext->m_captureindex[m_balancing] = balancing_index; + } + } + + pContext->m_capturestack[index + 2] = -1; + pContext->m_capturestack[index + 3] = 0; + } + else + { + pContext->m_capturestack[index + 3] --; + } + } + + return 0; +} + +// +// Deletage +// +template class CDelegateElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CDelegateElxT(int ndata = 0); + +public: + ElxInterface * m_pelx; + int m_ndata; // +0 : recursive to + // -3 : named recursive + + CBufferT m_szNamed; +}; + +template CDelegateElxT :: CDelegateElxT(int ndata) +{ + m_pelx = 0; + m_ndata = ndata; +} + +template int CDelegateElxT :: Match(CContext * pContext) const +{ + if(m_pelx != 0) + { + if(pContext->m_nCursiveLimit > 0) + { + pContext->m_nCursiveLimit --; + int result = m_pelx->Match(pContext); + pContext->m_nCursiveLimit ++; + return result; + } + else + return 0; + } + else + return 1; +} + +template int CDelegateElxT :: MatchNext(CContext * pContext) const +{ + if(m_pelx != 0) + return m_pelx->MatchNext(pContext); + else + return 0; +} + +// +// Empty +// +template class CEmptyElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CEmptyElxT(); +}; + +typedef CEmptyElxT <0> CEmptyElx; + +// +// Global +// +template class CGlobalElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CGlobalElxT(); +}; + +typedef CGlobalElxT <0> CGlobalElx; + +// +// Repeat +// +template class CRepeatElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CRepeatElxT(ElxInterface * pelx, int ntimes); + +protected: + int MatchFixed (CContext * pContext) const; + int MatchNextFixed(CContext * pContext) const; + int MatchForward (CContext * pContext) const + { + CContextShot shot(pContext); + + if( ! m_pelx->Match(pContext) ) + return 0; + + if(pContext->m_nCurrentPos != shot.m_nCurrentPos) + return 1; + + if( ! m_pelx->MatchNext(pContext) ) + return 0; + + if(pContext->m_nCurrentPos != shot.m_nCurrentPos) + return 1; + + shot.Restore(pContext); + return 0; + } + +public: + ElxInterface * m_pelx; + int m_nfixed; +}; + +typedef CRepeatElxT <0> CRepeatElx; + +// +// Greedy +// +template class CGreedyElxT : public CRepeatElxT +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CGreedyElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); + +protected: + int MatchVart (CContext * pContext) const; + int MatchNextVart(CContext * pContext) const; + +public: + int m_nvart; +}; + +typedef CGreedyElxT <0> CGreedyElx; + +// +// Independent +// +template class CIndependentElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CIndependentElxT(ElxInterface * pelx); + +public: + ElxInterface * m_pelx; +}; + +typedef CIndependentElxT <0> CIndependentElx; + +// +// List +// +template class CListElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CListElxT(int brightleft); + +public: + CBufferT m_elxlist; + int m_brightleft; +}; + +typedef CListElxT <0> CListElx; + +// +// Posix Elx +// +template class CPosixElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CPosixElxT(const char * posix, int brightleft); + +public: + POSIX_FUNC m_posixfun; + int m_brightleft; + int m_byes; +}; + +// +// Implementation +// +template CPosixElxT :: CPosixElxT(const char * posix, int brightleft) +{ + m_brightleft = brightleft; + + if(posix[1] == '^') + { + m_byes = 0; + posix += 2; + } + else + { + m_byes = 1; + posix += 1; + } + + if (!strncmp(posix, "alnum:", 6)) m_posixfun = ::isalnum ; + else if(!strncmp(posix, "alpha:", 6)) m_posixfun = ::isalpha ; + else if(!strncmp(posix, "ascii:", 6)) m_posixfun = ::isascii ; + else if(!strncmp(posix, "cntrl:", 6)) m_posixfun = ::iscntrl ; + else if(!strncmp(posix, "digit:", 6)) m_posixfun = ::isdigit ; + else if(!strncmp(posix, "graph:", 6)) m_posixfun = ::isgraph ; + else if(!strncmp(posix, "lower:", 6)) m_posixfun = ::islower ; + else if(!strncmp(posix, "print:", 6)) m_posixfun = ::isprint ; + else if(!strncmp(posix, "punct:", 6)) m_posixfun = ::ispunct ; + else if(!strncmp(posix, "space:", 6)) m_posixfun = ::isspace ; + else if(!strncmp(posix, "upper:", 6)) m_posixfun = ::isupper ; + else if(!strncmp(posix, "xdigit:",7)) m_posixfun = ::isxdigit; + else if(!strncmp(posix, "blank:", 6)) m_posixfun = _isblank ; + else m_posixfun = 0 ; +} + +inline int _isblank(int c) +{ + return c == 0x20 || c == '\t'; +} + +template int CPosixElxT :: Match(CContext * pContext) const +{ + if(m_posixfun == 0) return 0; + + int tlen = pContext->m_pMatchStringLength; + int npos = pContext->m_nCurrentPos; + + // check + int at = m_brightleft ? npos - 1 : npos; + if( at < 0 || at >= tlen ) + return 0; + + CHART ch = ((const CHART *)pContext->m_pMatchString)[at]; + + int bsucc = (*m_posixfun)(ch); + + if( ! m_byes ) + bsucc = ! bsucc; + + if( bsucc ) + pContext->m_nCurrentPos += m_brightleft ? -1 : 1; + + return bsucc; +} + +template int CPosixElxT :: MatchNext(CContext * pContext) const +{ + pContext->m_nCurrentPos -= m_brightleft ? -1 : 1; + return 0; +} + +// +// Possessive +// +template class CPossessiveElxT : public CGreedyElxT +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CPossessiveElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); +}; + +typedef CPossessiveElxT <0> CPossessiveElx; + +// +// Range Elx +// +template class CRangeElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CRangeElxT(int brightleft, int byes); + +public: + int IsContainChar(CHART ch) const; + +public: + CBufferT m_ranges; + CBufferT m_chars; + CBufferT m_embeds; + +public: + int m_brightleft; + int m_byes; +}; + +// +// Implementation +// +template CRangeElxT :: CRangeElxT(int brightleft, int byes) +{ + m_brightleft = brightleft; + m_byes = byes; +} + +template int CRangeElxT :: Match(CContext * pContext) const +{ + int tlen = pContext->m_pMatchStringLength; + int npos = pContext->m_nCurrentPos; + + // check + int at = m_brightleft ? npos - 1 : npos; + if( at < 0 || at >= tlen ) + return 0; + + CHART ch = ((const CHART *)pContext->m_pMatchString)[at]; + int bsucc = 0, i; + + // compare + for(i=0; !bsucc && iMatch(pContext)) + { + pContext->m_nCurrentPos = npos; + bsucc = 1; + } + } + + if( ! m_byes ) + bsucc = ! bsucc; + + if( bsucc ) + pContext->m_nCurrentPos += m_brightleft ? -1 : 1; + + return bsucc; +} + +template int CRangeElxT :: IsContainChar(CHART ch) const +{ + int bsucc = 0, i; + + // compare + for(i=0; !bsucc && i int CRangeElxT :: MatchNext(CContext * pContext) const +{ + pContext->m_nCurrentPos -= m_brightleft ? -1 : 1; + return 0; +} + +// +// Reluctant +// +template class CReluctantElxT : public CRepeatElxT +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CReluctantElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX); + +protected: + int MatchVart (CContext * pContext) const; + int MatchNextVart(CContext * pContext) const; + +public: + int m_nvart; +}; + +typedef CReluctantElxT <0> CReluctantElx; + +// +// String Elx +// +template class CStringElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CStringElxT(const CHART * fixed, int nlength, int brightleft, int bignorecase); + +public: + CBufferT m_szPattern; + int m_brightleft; + int m_bignorecase; +}; + +// +// Implementation +// +template CStringElxT :: CStringElxT(const CHART * fixed, int nlength, int brightleft, int bignorecase) : m_szPattern(fixed, nlength) +{ + m_brightleft = brightleft; + m_bignorecase = bignorecase; +} + +template int CStringElxT :: Match(CContext * pContext) const +{ + const CHART * pcsz = (const CHART *)pContext->m_pMatchString; + int npos = pContext->m_nCurrentPos; + int tlen = pContext->m_pMatchStringLength; + int slen = m_szPattern.GetSize(); + + int bsucc; + + if(m_brightleft) + { + if(npos < slen) + return 0; + + if(m_bignorecase) + bsucc = ! m_szPattern.nCompareNoCase(pcsz + (npos - slen)); + else + bsucc = ! m_szPattern.nCompare (pcsz + (npos - slen)); + + if( bsucc ) + pContext->m_nCurrentPos -= slen; + } + else + { + if(npos + slen > tlen) + return 0; + + if(m_bignorecase) + bsucc = ! m_szPattern.nCompareNoCase(pcsz + npos); + else + bsucc = ! m_szPattern.nCompare (pcsz + npos); + + if( bsucc ) + pContext->m_nCurrentPos += slen; + } + + return bsucc; +} + +template int CStringElxT :: MatchNext(CContext * pContext) const +{ + int slen = m_szPattern.GetSize(); + + if(m_brightleft) + pContext->m_nCurrentPos += slen; + else + pContext->m_nCurrentPos -= slen; + + return 0; +} + +// +// CConditionElx +// +template class CConditionElxT : public ElxInterface +{ +public: + int Match (CContext * pContext) const; + int MatchNext(CContext * pContext) const; + +public: + CConditionElxT(); + +public: + // backref condition + int m_nnumber; + CBufferT m_szNamed; + + // elx condition + ElxInterface * m_pelxask; + + // selection + ElxInterface * m_pelxyes, * m_pelxno; +}; + +template CConditionElxT :: CConditionElxT() +{ + m_nnumber = -1; +} + +template int CConditionElxT :: Match(CContext * pContext) const +{ + // status + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + + // condition result + int condition_yes = 0; + + // backref type + if( m_nnumber >= 0 ) + { + do + { + if(m_nnumber >= pContext->m_captureindex.GetSize()) break; + + int index = pContext->m_captureindex[m_nnumber]; + if( index < 0) break; + + // else valid + condition_yes = 1; + } + while(0); + } + else + { + if( m_pelxask == 0 ) + condition_yes = 1; + else + condition_yes = m_pelxask->Match(pContext); + + pContext->m_stack.Restore(nsize); + pContext->m_nCurrentPos = nbegin; + } + + // elx result + int bsucc; + if( condition_yes ) + bsucc = m_pelxyes == 0 ? 1 : m_pelxyes->Match(pContext); + else + bsucc = m_pelxno == 0 ? 1 : m_pelxno ->Match(pContext); + + if( bsucc ) + { + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(condition_yes); + } + else + { + pContext->m_capturestack.Restore(ncsize); + } + + return bsucc; +} + +template int CConditionElxT :: MatchNext(CContext * pContext) const +{ + // pop + int ncsize, condition_yes; + + pContext->m_stack.Pop(condition_yes); + pContext->m_stack.Pop(ncsize); + + // elx result + int bsucc; + if( condition_yes ) + bsucc = m_pelxyes == 0 ? 0 : m_pelxyes->MatchNext(pContext); + else + bsucc = m_pelxno == 0 ? 0 : m_pelxno ->MatchNext(pContext); + + if( bsucc ) + { + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(condition_yes); + } + else + { + pContext->m_capturestack.Restore(ncsize); + } + + return bsucc; +} + +// +// MatchResult +// +template class MatchResultT +{ +public: + int IsMatched() const; + +public: + int GetStart() const; + int GetEnd () const; + +public: + int MaxGroupNumber() const; + int GetGroupStart(int nGroupNumber) const; + int GetGroupEnd (int nGroupNumber) const; + +public: + MatchResultT(const MatchResultT & from) { *this = from; } + MatchResultT(CContext * pContext = 0, int nMaxNumber = -1); + MatchResultT & operator = (const MatchResultT &); + inline operator int() const { return IsMatched(); } + +public: + CBufferT m_result; +}; + +typedef MatchResultT <0> MatchResult; + +// Stocked Elx IDs +enum STOCKELX_ID_DEFINES +{ + STOCKELX_EMPTY = 0, + + /////////////////////// + + STOCKELX_DOT_ALL, + STOCKELX_DOT_NOT_ALL, + + STOCKELX_WORD, + STOCKELX_WORD_NOT, + + STOCKELX_SPACE, + STOCKELX_SPACE_NOT, + + STOCKELX_DIGITAL, + STOCKELX_DIGITAL_NOT, + + ////////////////////// + + STOCKELX_DOT_ALL_RIGHTLEFT, + STOCKELX_DOT_NOT_ALL_RIGHTLEFT, + + STOCKELX_WORD_RIGHTLEFT, + STOCKELX_WORD_RIGHTLEFT_NOT, + + STOCKELX_SPACE_RIGHTLEFT, + STOCKELX_SPACE_RIGHTLEFT_NOT, + + STOCKELX_DIGITAL_RIGHTLEFT, + STOCKELX_DIGITAL_RIGHTLEFT_NOT, + + ///////////////////// + + STOCKELX_COUNT +}; + +// REGEX_FLAGS +#ifndef _REGEX_FLAGS_DEFINED + enum REGEX_FLAGS + { + NO_FLAG = 0, + SINGLELINE = 0x01, + MULTILINE = 0x02, + GLOBAL = 0x04, + IGNORECASE = 0x08, + RIGHTTOLEFT = 0x10, + EXTENDED = 0x20 + }; + #define _REGEX_FLAGS_DEFINED +#endif + +// +// Builder T +// +template class CBuilderT +{ +public: + typedef CDelegateElxT CDelegateElx; + typedef CBracketElxT CBracketElx; + typedef CBackrefElxT CBackrefElx; + typedef CConditionElxT CConditionElx; + +// Methods +public: + ElxInterface * Build(const CBufferRefT & pattern, int flags); + int GetNamedNumber(const CBufferRefT & named) const; + void Clear(); + +public: + CBuilderT(); + ~CBuilderT(); + +// Public Attributes +public: + ElxInterface * m_pTopElx; + int m_nFlags; + int m_nMaxNumber; + int m_nNextNamed; + int m_nGroupCount; + int m_nNextBalancing; + + CBufferT m_objlist; + CBufferT m_grouplist; + CBufferT m_recursivelist; + CBufferT m_namedlist; + CBufferT m_namedbackreflist; + CBufferT m_namedconditionlist; + CBufferT m_purebalancinglist; + +// CHART_INFO +protected: + struct CHART_INFO + { + public: + CHART ch; + int type; + int pos; + int len; + + public: + CHART_INFO(CHART c, int t, int p = 0, int l = 0) { ch = c; type = t; pos = p; len = l; } + inline int operator == (const CHART_INFO & ci) { return ch == ci.ch && type == ci.type; } + inline int operator != (const CHART_INFO & ci) { return ! operator == (ci); } + }; + +protected: + static unsigned int Hex2Int(const CHART * pcsz, int length, int & used); + static int ReadDec(char * & str, unsigned int & dec); + void MoveNext(); + int GetNext2(); + + ElxInterface * BuildAlternative(int vaflags); + ElxInterface * BuildList (int & flags); + ElxInterface * BuildRepeat (int & flags); + ElxInterface * BuildSimple (int & flags); + ElxInterface * BuildCharset (int & flags); + ElxInterface * BuildRecursive (int & flags); + ElxInterface * BuildBoundary (int & flags); + ElxInterface * BuildBackref (int & flags); + + ElxInterface * GetStockElx (int nStockId); + ElxInterface * Keep(ElxInterface * pElx); + +// Private Attributes +protected: + CBufferRefT m_pattern; + CHART_INFO prev, curr, next, nex2; + int m_nNextPos; + int m_nCharsetDepth; + int m_bQuoted; + POSIX_FUNC m_quote_fun; + + // Backup current pos + struct Snapshot + { + CHART_INFO prev, curr, next, nex2; + int m_nNextPos; + int m_nCharsetDepth; + int m_bQuoted; + POSIX_FUNC m_quote_fun; + Snapshot():prev(0,0),curr(0,0),next(0,0),nex2(0,0) {} + }; + void Backup (Snapshot * pdata) { memcpy(pdata, &prev, sizeof(Snapshot)); } + void Restore(Snapshot * pdata) { memcpy(&prev, pdata, sizeof(Snapshot)); } + + ElxInterface * m_pStockElxs[STOCKELX_COUNT]; +}; + +// +// Implementation +// +template CBuilderT :: CBuilderT() : m_pattern(0, 0), prev(0, 0), curr(0, 0), next(0, 0), nex2(0, 0) +{ + Clear(); +} + +template CBuilderT :: ~CBuilderT() +{ + Clear(); +} + +template int CBuilderT :: GetNamedNumber(const CBufferRefT & named) const +{ + for(int i=0; im_elxlist[0])->m_szNamed.CompareNoCase(named) ) + return ((CBracketElx *)m_namedlist[i]->m_elxlist[0])->m_nnumber; + } + + return -3; +} + +template ElxInterface * CBuilderT :: Build(const CBufferRefT & pattern, int flags) +{ + // init + m_pattern = pattern; + m_nNextPos = 0; + m_nCharsetDepth = 0; + m_nMaxNumber = 0; + m_nNextNamed = 0; + m_nNextBalancing= 0; + m_nFlags = flags; + m_bQuoted = 0; + m_quote_fun = 0; + + m_grouplist .Restore(0); + m_recursivelist .Restore(0); + m_namedlist .Restore(0); + m_namedbackreflist .Restore(0); + m_namedconditionlist.Restore(0); + m_purebalancinglist .Restore(0); + + int i; + for(i=0; i<3; i++) MoveNext(); + + // build + m_pTopElx = BuildAlternative(flags); + + // group 0 + m_grouplist.Prepare(0); + m_grouplist[0] = m_pTopElx; + + // append named to unnamed + m_nGroupCount = m_grouplist.GetSize(); + + m_grouplist.Prepare(m_nMaxNumber + m_namedlist.GetSize()); + + for(i=0; im_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_namedlist[i]->m_elxlist[2]; + + // append + m_grouplist[m_nGroupCount ++] = m_namedlist[i]; + + if( pleft->m_nnumber > 0 ) + continue; + + // same name + int find_same_name = GetNamedNumber(pleft->m_szNamed); + if( find_same_name >= 0 ) + { + pleft ->m_nnumber = find_same_name; + pright->m_nnumber = find_same_name; + } + else + { + m_nMaxNumber ++; + + pleft ->m_nnumber = m_nMaxNumber; + pright->m_nnumber = m_nMaxNumber; + } + } + + for(i=0; im_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_namedlist[i]->m_elxlist[2]; + + // balancing + if(pleft->m_szBalancing.GetSize() > 0) + { + int balancing_to = GetNamedNumber(pleft->m_szBalancing); + if(balancing_to >= 0) + { + pleft ->m_balancing = balancing_to; + pright->m_balancing = balancing_to; + } + else + { + // TODO ERROR + } + } + } + + for(i=1; im_elxlist[0]; + + if( pleft->m_nnumber > m_nMaxNumber ) + m_nMaxNumber = pleft->m_nnumber; + } + + // pure balancing group + int nMaxNumber = m_nMaxNumber; + for(i=0; im_elxlist[0]; + CBracketElx * pright = (CBracketElx *)m_purebalancinglist[i]->m_elxlist[2]; + + nMaxNumber ++; + + pleft ->m_nnumber = nMaxNumber; + pright->m_nnumber = nMaxNumber; + + // balancing + if(pleft->m_szBalancing.GetSize() > 0) + { + int balancing_to = GetNamedNumber(pleft->m_szBalancing); + if(balancing_to >= 0) + { + pleft ->m_balancing = balancing_to; + pright->m_balancing = balancing_to; + } + else + { + // TODO ERROR + } + } + } + + // connect recursive + for(i=0; im_ndata == -3 ) + m_recursivelist[i]->m_ndata = GetNamedNumber(m_recursivelist[i]->m_szNamed); + + if( m_recursivelist[i]->m_ndata >= 0 && m_recursivelist[i]->m_ndata <= m_nMaxNumber ) + { + if( m_recursivelist[i]->m_ndata == 0 ) + m_recursivelist[i]->m_pelx = m_pTopElx; + else for(int j=1; jm_ndata == ((CBracketElx *)((CListElx*)m_grouplist[j])->m_elxlist[0])->m_nnumber) + { + m_recursivelist[i]->m_pelx = m_grouplist[j]; + break; + } + } + } + } + + // named backref + for(i=0; im_nnumber = GetNamedNumber(m_namedbackreflist[i]->m_szNamed); + } + + // named condition + for(i=0; im_szNamed); + if( nn >= 0 ) + { + m_namedconditionlist[i]->m_nnumber = nn; + m_namedconditionlist[i]->m_pelxask = 0; + } + } + + return m_pTopElx; +} + +template void CBuilderT :: Clear() +{ + for(int i=0; i unsigned int CBuilderT :: Hex2Int(const CHART * pcsz, int length, int & used) +{ + unsigned int result = 0; + int & i = used; + + for(i=0; i= RCHART('0') && pcsz[i] <= RCHART('9')) + result = (result << 4) + (pcsz[i] - RCHART('0')); + else if(pcsz[i] >= RCHART('A') && pcsz[i] <= RCHART('F')) + result = (result << 4) + (0x0A + (pcsz[i] - RCHART('A'))); + else if(pcsz[i] >= RCHART('a') && pcsz[i] <= RCHART('f')) + result = (result << 4) + (0x0A + (pcsz[i] - RCHART('a'))); + else + break; + } + + return result; +} + +template inline ElxInterface * CBuilderT :: Keep(ElxInterface * pelx) +{ + m_objlist.Push(pelx); + return pelx; +} + +template void CBuilderT :: MoveNext() +{ + // forwards + prev = curr; + curr = next; + next = nex2; + + // get nex2 + while( ! GetNext2() ) {}; +} + +template int CBuilderT :: GetNext2() +{ + // check length + if(m_nNextPos >= m_pattern.GetSize()) + { + nex2 = CHART_INFO(0, 1, m_nNextPos, 0); + return 1; + } + + int delta = 1; + CHART ch = m_pattern[m_nNextPos]; + + // if quoted + if(m_bQuoted) + { + if(ch == RCHART('\\')) + { + if(m_pattern[m_nNextPos + 1] == RCHART('E')) + { + m_quote_fun = 0; + m_bQuoted = 0; + m_nNextPos += 2; + return 0; + } + } + + if(m_quote_fun != 0) + nex2 = CHART_INFO((CHART)(*m_quote_fun)((int)ch), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + + m_nNextPos += delta; + + return 1; + } + + // common + switch(ch) + { + case RCHART('\\'): + { + CHART ch1 = m_pattern[m_nNextPos+1]; + + // backref + if(ch1 >= RCHART('0') && ch1 <= RCHART('9')) + { + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + break; + } + + // escape + delta = 2; + + switch(ch1) + { + case RCHART('A'): + case RCHART('Z'): + case RCHART('z'): + case RCHART('w'): + case RCHART('W'): + case RCHART('s'): + case RCHART('S'): + case RCHART('B'): + case RCHART('d'): + case RCHART('D'): + case RCHART('k'): + case RCHART('g'): + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + + case RCHART('b'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO('\b', 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + + /* + case RCHART('<'): + case RCHART('>'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + */ + + case RCHART('x'): + if(m_pattern[m_nNextPos+2] != '{') + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 2, red); + + delta += red; + + if(red > 0) + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + + break; + } + + case RCHART('u'): + if(m_pattern[m_nNextPos+2] != '{') + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 4, red); + + delta += red; + + if(red > 0) + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + } + else + { + int red = 0; + unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 3, sizeof(int) * 2, red); + + delta += red; + + while(m_nNextPos + delta < m_pattern.GetSize() && m_pattern.At(m_nNextPos + delta) != RCHART('}')) + delta ++; + + delta ++; // skip '}' + + nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta); + } + break; + + case RCHART('a'): nex2 = CHART_INFO(RCHART('\a'), 0, m_nNextPos, delta); break; + case RCHART('f'): nex2 = CHART_INFO(RCHART('\f'), 0, m_nNextPos, delta); break; + case RCHART('n'): nex2 = CHART_INFO(RCHART('\n'), 0, m_nNextPos, delta); break; + case RCHART('r'): nex2 = CHART_INFO(RCHART('\r'), 0, m_nNextPos, delta); break; + case RCHART('t'): nex2 = CHART_INFO(RCHART('\t'), 0, m_nNextPos, delta); break; + case RCHART('v'): nex2 = CHART_INFO(RCHART('\v'), 0, m_nNextPos, delta); break; + case RCHART('e'): nex2 = CHART_INFO(RCHART( 27 ), 0, m_nNextPos, delta); break; + + case RCHART('G'): // skip '\G' + if(m_nCharsetDepth > 0) + { + m_nNextPos += 2; + return 0; + } + else + { + nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta); + break; + } + + case RCHART('L'): + if( ! m_quote_fun ) m_quote_fun = ::tolower; + + case RCHART('U'): + if( ! m_quote_fun ) m_quote_fun = ::toupper; + + case RCHART('Q'): + { + m_bQuoted = 1; + m_nNextPos += 2; + return 0; + } + + case RCHART('E'): + { + m_quote_fun = 0; + m_bQuoted = 0; + m_nNextPos += 2; + return 0; + } + + case 0: + if(m_nNextPos+1 >= m_pattern.GetSize()) + { + delta = 1; + nex2 = CHART_INFO(ch , 0, m_nNextPos, delta); + } + else + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); // common '\0' char + break; + + default: + nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); + break; + } + } + break; + + case RCHART('*'): + case RCHART('+'): + case RCHART('?'): + case RCHART('.'): + case RCHART('{'): + case RCHART('}'): + case RCHART(')'): + case RCHART('|'): + case RCHART('$'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + break; + + case RCHART('-'): + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case RCHART('('): + { + CHART ch1 = m_pattern[m_nNextPos+1]; + CHART ch2 = m_pattern[m_nNextPos+2]; + + // skip remark + if(ch1 == RCHART('?') && ch2 == RCHART('#')) + { + m_nNextPos += 2; + while(m_nNextPos < m_pattern.GetSize()) + { + if(m_pattern[m_nNextPos] == RCHART(')')) + break; + + m_nNextPos ++; + } + + if(m_pattern[m_nNextPos] == RCHART(')')) + { + m_nNextPos ++; + + // get next nex2 + return 0; + } + } + else + { + if(m_nCharsetDepth > 0) + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + } + break; + + case RCHART('#'): + if(m_nFlags & EXTENDED) + { + // skip remark + m_nNextPos ++; + + while(m_nNextPos < m_pattern.GetSize()) + { + if(m_pattern[m_nNextPos] == RCHART('\n') || m_pattern[m_nNextPos] == RCHART('\r')) + break; + + m_nNextPos ++; + } + + // get next nex2 + return 0; + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(' '): + case RCHART('\f'): + case RCHART('\n'): + case RCHART('\r'): + case RCHART('\t'): + case RCHART('\v'): + if(m_nFlags & EXTENDED) + { + m_nNextPos ++; + + // get next nex2 + return 0; + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART('['): + if( m_nCharsetDepth == 0 || m_pattern.At(m_nNextPos + 1, 0) == RCHART(':') ) + { + m_nCharsetDepth ++; + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(']'): + if(m_nCharsetDepth > 0) + { + m_nCharsetDepth --; + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + } + else + { + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + } + break; + + case RCHART(':'): + if(next == CHART_INFO(RCHART('['), 1)) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case RCHART('^'): + if(m_nCharsetDepth == 0 || next == CHART_INFO(RCHART('['), 1) || (curr == CHART_INFO(RCHART('['), 1) && next == CHART_INFO(RCHART(':'), 1))) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + + case 0: + if(m_nNextPos >= m_pattern.GetSize()) + nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); // end of string + else + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); // common '\0' char + break; + + default: + nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); + break; + } + + m_nNextPos += delta; + + return 1; +} + +template ElxInterface * CBuilderT :: GetStockElx(int nStockId) +{ + ElxInterface ** pStockElxs = m_pStockElxs; + + // check + if(nStockId < 0 || nStockId >= STOCKELX_COUNT) + return GetStockElx(0); + + // create if no + if(pStockElxs[nStockId] == 0) + { + switch(nStockId) + { + case STOCKELX_EMPTY: + pStockElxs[nStockId] = Keep(new CEmptyElx()); + break; + + case STOCKELX_WORD: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DOT_ALL: + pStockElxs[nStockId] = Keep(new CRangeElxT (0, 0)); + break; + + case STOCKELX_DOT_NOT_ALL: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 1)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (0, 0)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_WORD_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z')); + pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z')); + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + pRange->m_chars .Push(RCHART('_')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DOT_ALL_RIGHTLEFT: + pStockElxs[nStockId] = Keep(new CRangeElxT (1, 0)); + break; + + case STOCKELX_DOT_NOT_ALL_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_chars .Push(RCHART('\n')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + pRange->m_chars .Push(RCHART('\f')); + pRange->m_chars .Push(RCHART('\v')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_SPACE_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_chars .Push(RCHART(' ')); + pRange->m_chars .Push(RCHART('\t')); + pRange->m_chars .Push(RCHART('\r')); + pRange->m_chars .Push(RCHART('\n')); + pRange->m_chars .Push(RCHART('\f')); + pRange->m_chars .Push(RCHART('\v')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_RIGHTLEFT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 1)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + + case STOCKELX_DIGITAL_RIGHTLEFT_NOT: + { + CRangeElxT * pRange = (CRangeElxT *)Keep(new CRangeElxT (1, 0)); + + pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9')); + + pStockElxs[nStockId] = pRange; + } + break; + } + } + + // return + return pStockElxs[nStockId]; +} + +template ElxInterface * CBuilderT :: BuildAlternative(int vaflags) +{ + if(curr == CHART_INFO(0, 1)) + return GetStockElx(STOCKELX_EMPTY); + + // flag instance + int flags = vaflags; + + // first part + ElxInterface * pAlternativeOne = BuildList(flags); + + // check alternative + if(curr == CHART_INFO(RCHART('|'), 1)) + { + CAlternativeElx * pAlternative = (CAlternativeElx *)Keep(new CAlternativeElx()); + pAlternative->m_elxlist.Push(pAlternativeOne); + + // loop + while(curr == CHART_INFO(RCHART('|'), 1)) + { + // skip '|' itself + MoveNext(); + + pAlternativeOne = BuildList(flags); + pAlternative->m_elxlist.Push(pAlternativeOne); + } + + return pAlternative; + } + + return pAlternativeOne; +} + +template ElxInterface * CBuilderT :: BuildList(int & flags) +{ + if(curr == CHART_INFO(0, 1) || curr == CHART_INFO(RCHART('|'), 1) || curr == CHART_INFO(RCHART(')'), 1)) + return GetStockElx(STOCKELX_EMPTY); + + // first + ElxInterface * pListOne = BuildRepeat(flags); + + if(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1)) + { + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + pList->m_elxlist.Push(pListOne); + + while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1)) + { + pListOne = BuildRepeat(flags); + + // add + pList->m_elxlist.Push(pListOne); + } + + return pList; + } + + return pListOne; +} + +template ElxInterface * CBuilderT :: BuildRepeat(int & flags) +{ + // simple + ElxInterface * pSimple = BuildSimple(flags); + + if(curr.type == 0) return pSimple; + + // is quantifier or not + int bIsQuantifier = 1; + + // quantifier range + unsigned int nMin = 0, nMax = 0; + + switch(curr.ch) + { + case RCHART('{'): + { + CBufferT re; + + // skip '{' + MoveNext(); + + // copy + while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('}'), 1)) + { + re.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + // skip '}' + MoveNext(); + + // read + int red; + char * str = re.GetBuffer(); + + if( ! ReadDec(str, nMin) ) + red = 0; + else if( *str != ',' ) + red = 1; + else + { + str ++; + + if( ! ReadDec(str, nMax) ) + red = 2; + else + red = 3; + } + + // check + if(red <= 1 ) nMax = nMin; + if(red == 2 ) nMax = INT_MAX; + if(nMax < nMin) nMax = nMin; + } + break; + + case RCHART('?'): + nMin = 0; + nMax = 1; + + // skip '?' + MoveNext(); + break; + + case RCHART('*'): + nMin = 0; + nMax = INT_MAX; + + // skip '*' + MoveNext(); + break; + + case RCHART('+'): + nMin = 1; + nMax = INT_MAX; + + // skip '+' + MoveNext(); + break; + + default: + bIsQuantifier = 0; + break; + } + + // do quantify + if(bIsQuantifier) + { + // 0 times + if(nMax == 0) + return GetStockElx(STOCKELX_EMPTY); + + // fixed times + if(nMin == nMax) + { + if(curr == CHART_INFO(RCHART('?'), 1) || curr == CHART_INFO(RCHART('+'), 1)) + MoveNext(); + + return Keep(new CRepeatElx(pSimple, nMin)); + } + + // range times + if(curr == CHART_INFO(RCHART('?'), 1)) + { + MoveNext(); + return Keep(new CReluctantElx(pSimple, nMin, nMax)); + } + else if(curr == CHART_INFO(RCHART('+'), 1)) + { + MoveNext(); + return Keep(new CPossessiveElx(pSimple, nMin, nMax)); + } + else + { + return Keep(new CGreedyElx(pSimple, nMin, nMax)); + } + } + + return pSimple; +} + +template ElxInterface * CBuilderT :: BuildSimple(int & flags) +{ + CBufferT fixed; + + while(curr != CHART_INFO(0, 1)) + { + if(curr.type == 0) + { + if(next == CHART_INFO(RCHART('{'), 1) || next == CHART_INFO(RCHART('?'), 1) || next == CHART_INFO(RCHART('*'), 1) || next == CHART_INFO(RCHART('+'), 1)) + { + if(fixed.GetSize() == 0) + { + fixed.Append(curr.ch, 1); + MoveNext(); + } + + break; + } + else + { + fixed.Append(curr.ch, 1); + MoveNext(); + } + } + else if(curr.type == 1) + { + CHART vch = curr.ch; + + // end of simple + if(vch == RCHART(')') || vch == RCHART('|')) + break; + + // has fixed already + if(fixed.GetSize() > 0) + break; + + // left parentheses + if(vch == RCHART('(')) + { + return BuildRecursive(flags); + } + + // char set + if( vch == RCHART('[') || vch == RCHART('.') || vch == RCHART('w') || vch == RCHART('W') || + vch == RCHART('s') || vch == RCHART('S') || vch == RCHART('d') || vch == RCHART('D') + ) + { + return BuildCharset(flags); + } + + // boundary + if( vch == RCHART('^') || vch == RCHART('$') || vch == RCHART('A') || vch == RCHART('Z') || vch == RCHART('z') || + vch == RCHART('b') || vch == RCHART('B') || vch == RCHART('G') // vch == RCHART('<') || vch == RCHART('>') + ) + { + return BuildBoundary(flags); + } + + // backref + if(vch == RCHART('\\') || vch == RCHART('k') || vch == RCHART('g')) + { + return BuildBackref(flags); + } + + // treat vchar as char + fixed.Append(curr.ch, 1); + MoveNext(); + } + } + + if(fixed.GetSize() > 0) + return Keep(new CStringElxT (fixed.GetBuffer(), fixed.GetSize(), flags & RIGHTTOLEFT, flags & IGNORECASE)); + else + return GetStockElx(STOCKELX_EMPTY); +} + +#define deelx_max(a, b) (((a) > (b)) ? (a) : (b)) +#define deelx_min(a, b) (((a) < (b)) ? (a) : (b)) + +template ElxInterface * CBuilderT :: BuildCharset(int & flags) +{ + // char + CHART ch = curr.ch; + + // skip + MoveNext(); + + switch(ch) + { + case RCHART('.'): + return GetStockElx( + flags & RIGHTTOLEFT ? + ((flags & SINGLELINE) ? STOCKELX_DOT_ALL_RIGHTLEFT : STOCKELX_DOT_NOT_ALL_RIGHTLEFT) : + ((flags & SINGLELINE) ? STOCKELX_DOT_ALL : STOCKELX_DOT_NOT_ALL) + ); + + case RCHART('w'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_WORD_RIGHTLEFT : STOCKELX_WORD); + + case RCHART('W'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_WORD_RIGHTLEFT_NOT : STOCKELX_WORD_NOT); + + case RCHART('s'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_SPACE_RIGHTLEFT : STOCKELX_SPACE); + + case RCHART('S'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_SPACE_RIGHTLEFT_NOT : STOCKELX_SPACE_NOT); + + case RCHART('d'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_DIGITAL_RIGHTLEFT : STOCKELX_DIGITAL); + + case RCHART('D'): + return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_DIGITAL_RIGHTLEFT_NOT : STOCKELX_DIGITAL_NOT); + + case RCHART('['): + { + CRangeElxT * pRange; + + // create + if(curr == CHART_INFO(RCHART(':'), 1)) + { + // Backup before posix + Snapshot shot; + Backup(&shot); + + CBufferT posix; + + do { + posix.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + while(curr.ch != RCHART(0) && curr != CHART_INFO(RCHART(']'), 1)); + + MoveNext(); // skip ']' + + // posix + CPosixElxT * pposix = (CPosixElxT *) Keep(new CPosixElxT (posix.GetBuffer(), flags & RIGHTTOLEFT)); + if(pposix->m_posixfun != 0) + { + return pposix; + } + + // restore if not posix + Restore(&shot); + } + + if(curr == CHART_INFO(RCHART('^'), 1)) + { + MoveNext(); // skip '^' + pRange = (CRangeElxT *)Keep(new CRangeElxT (flags & RIGHTTOLEFT, 0)); + } + else + { + pRange = (CRangeElxT *)Keep(new CRangeElxT (flags & RIGHTTOLEFT, 1)); + } + + // parse + while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART(']'), 1)) + { + ch = curr.ch; + + if(curr.type == 1 && ( + ch == RCHART('.') || ch == RCHART('w') || ch == RCHART('W') || ch == RCHART('s') || ch == RCHART('S') || ch == RCHART('d') || ch == RCHART('D') || + (ch == RCHART('[') && next == CHART_INFO(RCHART(':'), 1)) + )) + { + pRange->m_embeds.Push(BuildCharset(flags)); + } + else if(next == CHART_INFO(RCHART('-'), 1) && nex2.type == 0) + { + pRange->m_ranges.Push(ch); pRange->m_ranges.Push(nex2.ch); + + // next + MoveNext(); + MoveNext(); + MoveNext(); + } + else + { + pRange->m_chars.Push(ch); + + // next + MoveNext(); + } + } + + // skip ']' + MoveNext(); + + if( flags & IGNORECASE ) + { + CBufferT & ranges = pRange->m_ranges; + int i, oldcount = ranges.GetSize() / 2; + + for(i=0; i= RCHART('A') ) + { + newmin = tolower( deelx_max(RCHART('A'), ranges[i*2 ]) ); + newmax = tolower( deelx_min(RCHART('Z'), ranges[i*2+1]) ); + + if( newmin < ranges[i*2] || newmax > ranges[i*2+1] ) + { + ranges.Push(newmin); + ranges.Push(newmax); + } + } + + if( ranges[i*2] <= RCHART('z') && ranges[i*2+1] >= RCHART('a') ) + { + newmin = toupper( deelx_max(RCHART('a'), ranges[i*2 ]) ); + newmax = toupper( deelx_min(RCHART('z'), ranges[i*2+1]) ); + + if( newmin < ranges[i*2] || newmax > ranges[i*2+1] ) + { + ranges.Push(newmin); + ranges.Push(newmax); + } + } + } + + CBufferT & chars = pRange->m_chars; + oldcount = chars.GetSize(); + for(i=0; iIsContainChar(tolower(chars[i])) ) + chars.Push(tolower(chars[i])); + + if( islower(chars[i]) && ! pRange->IsContainChar(toupper(chars[i])) ) + chars.Push(toupper(chars[i])); + } + } + + return pRange; + } + } + + return GetStockElx(STOCKELX_EMPTY); +} + +template ElxInterface * CBuilderT :: BuildRecursive(int & flags) +{ + // skip '(' + MoveNext(); + + if(curr == CHART_INFO(RCHART('?'), 1)) + { + ElxInterface * pElx = 0; + + // skip '?' + MoveNext(); + + int bNegative = 0; + CHART named_end = RCHART('>'); + + switch(curr.ch) + { + case RCHART('!'): + bNegative = 1; + + case RCHART('='): + { + MoveNext(); // skip '!' or '=' + pElx = Keep(new CAssertElx(BuildAlternative(flags & ~RIGHTTOLEFT), !bNegative)); + } + break; + + case RCHART('<'): + switch(next.ch) + { + case RCHART('!'): + bNegative = 1; + + case RCHART('='): + MoveNext(); // skip '<' + MoveNext(); // skip '!' or '=' + { + pElx = Keep(new CAssertElx(BuildAlternative(flags | RIGHTTOLEFT), !bNegative)); + } + break; + + default: // named group + break; + } + // break if assertion // else named + if(pElx != 0) break; + + case RCHART('P'): + if(curr.ch == RCHART('P')) MoveNext(); // skip 'P' + + case RCHART('\''): + if (curr.ch == RCHART('<' )) named_end = RCHART('>' ); + else if(curr.ch == RCHART('\'')) named_end = RCHART('\''); + MoveNext(); // skip '<' or '\'' + { + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + CBracketElx * pleft = (CBracketElx *)Keep(new CBracketElx(-1, flags & RIGHTTOLEFT ? 1 : 0)); + CBracketElx * pright = (CBracketElx *)Keep(new CBracketElx(-1, flags & RIGHTTOLEFT ? 0 : 1)); + + // save name + CBufferT & name = pleft->m_szNamed, & balancing_name = pleft->m_szBalancing, * pname = &name; + CBufferT num, balancing_num, * pnum = # + + while(curr.ch != RCHART(0) && curr.ch != named_end) + { + if(curr.ch == RCHART('-')) + { + pname = &balancing_name; + pnum = &balancing_num; + MoveNext(); + continue; + } + + pname->Append(curr.ch, 1); + pnum ->Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pleft ->m_nnumber = number; + pright->m_nnumber = number; + + name.Release(); + } + + str = balancing_num.GetBuffer(); + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pleft ->m_balancing = number; + pright->m_balancing = number; + + balancing_name.Release(); + } + + // left, center, right + pList->m_elxlist.Push(pleft); + pList->m_elxlist.Push(BuildAlternative(flags)); + pList->m_elxlist.Push(pright); + + // named number + if(pleft->m_nnumber >= 0 || name.GetSize() > 0) + { + int nThisBackref = m_nNextNamed ++; + m_namedlist.Prepare(nThisBackref); + m_namedlist[nThisBackref] = pList; + } + else if(pleft->m_balancing >= 0 || balancing_name.GetSize() > 0) + { + int nThisBalancing = m_nNextBalancing ++; + m_purebalancinglist.Prepare(nThisBalancing, 0); + m_purebalancinglist[nThisBalancing] = pList; + } + else + { + // TODO ERROR + } + + pElx = pList; + } + break; + + case RCHART('>'): + { + MoveNext(); // skip '>' + pElx = Keep(new CIndependentElx(BuildAlternative(flags))); + } + break; + + case RCHART('R'): + MoveNext(); // skip 'R' + while(curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space + + if(curr.ch == RCHART('<') || curr.ch == RCHART('\'')) + { + named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\''); + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(-3)); + + MoveNext(); // skip '<' or '\\' + + // save name + CBufferT & name = pDelegate->m_szNamed; + CBufferT num; + + while(curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pDelegate->m_ndata = number; + name.Release(); + } + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + else + { + CBufferT rto; + while(curr.ch != RCHART(0) && curr.ch != RCHART(')')) + { + rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + unsigned int rtono = 0; + char * str = rto.GetBuffer(); + ReadDec(str, rtono); + + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono)); + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + break; + + case RCHART('('): + { + CConditionElx * pConditionElx = (CConditionElx *)Keep(new CConditionElx()); + + // condition + ElxInterface * & pCondition = pConditionElx->m_pelxask; + + if(next == CHART_INFO(RCHART('?'), 1)) + { + pCondition = BuildRecursive(flags); + } + else // named, assert or number + { + MoveNext(); // skip '(' + int pos0 = curr.pos; + + // save elx condition + pCondition = Keep(new CAssertElx(BuildAlternative(flags), 1)); + + // save name + pConditionElx->m_szNamed.Append(m_pattern.GetBuffer() + pos0, curr.pos - pos0, 1); + + // save number + CBufferT numstr; + while(pos0 < curr.pos) + { + CHART ch = m_pattern[pos0]; + numstr.Append(((ch & (CHART)0xff) == ch) ? (char)ch : 0, 1); + pos0 ++; + } + + unsigned int number; + char * str = numstr.GetBuffer(); + + // valid group number + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pConditionElx->m_nnumber = number; + pCondition = 0; + } + else // maybe elx, maybe named + { + pConditionElx->m_nnumber = -1; + m_namedconditionlist.Push(pConditionElx); + } + + MoveNext(); // skip ')' + } + + // alternative + { + int newflags = flags; + + pConditionElx->m_pelxyes = BuildList(newflags); + } + + if(curr.ch == RCHART('|')) + { + MoveNext(); // skip '|' + + pConditionElx->m_pelxno = BuildAlternative(flags); + } + else + { + pConditionElx->m_pelxno = 0; + } + + pElx = pConditionElx; + } + break; + + default: + while(curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space + + if(curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) // recursive (?1) => (?R1) + { + CBufferT rto; + while(curr.ch != RCHART(0) && curr.ch != RCHART(')')) + { + rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + + unsigned int rtono = 0; + char * str = rto.GetBuffer(); + ReadDec(str, rtono); + + CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono)); + + m_recursivelist.Push(pDelegate); + pElx = pDelegate; + } + else + { + // flag + int newflags = flags; + while(curr != CHART_INFO(0, 1) && curr.ch != RCHART(':') && curr.ch != RCHART(')') && curr != CHART_INFO(RCHART('('), 1)) + { + int tochange = 0; + + switch(curr.ch) + { + case RCHART('i'): + case RCHART('I'): + tochange = IGNORECASE; + break; + + case RCHART('s'): + case RCHART('S'): + tochange = SINGLELINE; + break; + + case RCHART('m'): + case RCHART('M'): + tochange = MULTILINE; + break; + + case RCHART('g'): + case RCHART('G'): + tochange = GLOBAL; + break; + + case RCHART('-'): + bNegative = 1; + break; + } + + if(bNegative) + newflags &= ~tochange; + else + newflags |= tochange; + + // move to next char + MoveNext(); + } + + if(curr.ch == RCHART(':') || curr == CHART_INFO(RCHART('('), 1)) + { + // skip ':' + if(curr.ch == RCHART(':')) MoveNext(); + + pElx = BuildAlternative(newflags); + } + else + { + // change parent flags + flags = newflags; + + pElx = GetStockElx(STOCKELX_EMPTY); + } + } + break; + } + + MoveNext(); // skip ')' + + return pElx; + } + else + { + // group and number + CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT)); + int nThisBackref = ++ m_nMaxNumber; + + // left, center, right + pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, flags & RIGHTTOLEFT ? 1 : 0))); + pList->m_elxlist.Push(BuildAlternative(flags)); + pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, flags & RIGHTTOLEFT ? 0 : 1))); + + // for recursive + m_grouplist.Prepare(nThisBackref); + m_grouplist[nThisBackref] = pList; + + // right + MoveNext(); // skip ')' + + return pList; + } +} + +template ElxInterface * CBuilderT :: BuildBoundary(int & flags) +{ + // char + CHART ch = curr.ch; + + // skip + MoveNext(); + + switch(ch) + { + case RCHART('^'): + return Keep(new CBoundaryElxT ((flags & MULTILINE) ? BOUNDARY_LINE_BEGIN : BOUNDARY_FILE_BEGIN)); + + case RCHART('$'): + return Keep(new CBoundaryElxT ((flags & MULTILINE) ? BOUNDARY_LINE_END : BOUNDARY_FILE_END)); + + case RCHART('b'): + return Keep(new CBoundaryElxT (BOUNDARY_WORD_EDGE)); + + case RCHART('B'): + return Keep(new CBoundaryElxT (BOUNDARY_WORD_EDGE, 0)); + + case RCHART('A'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_BEGIN)); + + case RCHART('Z'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_END_N)); + + case RCHART('z'): + return Keep(new CBoundaryElxT (BOUNDARY_FILE_END)); + + case RCHART('G'): + if(flags & GLOBAL) + return Keep(new CGlobalElx()); + else + return GetStockElx(STOCKELX_EMPTY); + + default: + return GetStockElx(STOCKELX_EMPTY); + } +} + +template ElxInterface * CBuilderT :: BuildBackref(int & flags) +{ + // skip '\\' or '\k' or '\g' + MoveNext(); + + if(curr.ch == RCHART('<') || curr.ch == RCHART('\'')) + { + CHART named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\''); + CBackrefElxT * pbackref = (CBackrefElxT *)Keep(new CBackrefElxT (-1, flags & RIGHTTOLEFT, flags & IGNORECASE)); + + MoveNext(); // skip '<' or '\'' + + // save name + CBufferT & name = pbackref->m_szNamed; + CBufferT num; + + while(curr.ch != RCHART(0) && curr.ch != named_end) + { + name.Append(curr.ch, 1); + num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1); + MoveNext(); + } + MoveNext(); // skip '>' or '\'' + + // check + unsigned int number; + char * str = num.GetBuffer(); + + if( ReadDec(str, number) ? ( *str == '\0') : 0 ) + { + pbackref->m_nnumber = number; + name.Release(); + } + else + { + m_namedbackreflist.Push(pbackref); + } + + return pbackref; + } + else + { + unsigned int nbackref = 0; + + for(int i=0; i<3; i++) + { + if(curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) + nbackref = nbackref * 10 + (curr.ch - RCHART('0')); + else + break; + + MoveNext(); + } + + return Keep(new CBackrefElxT (nbackref, flags & RIGHTTOLEFT, flags & IGNORECASE)); + } +} + +template int CBuilderT :: ReadDec(char * & str, unsigned int & dec) +{ + int s = 0; + while(str[s] != 0 && isspace(str[s])) s++; + + if(str[s] < '0' || str[s] > '9') return 0; + + dec = 0; + unsigned int i; + + for(i = s; i= '0' && str[i] <= '9') + dec = dec * 10 + (str[i] - '0'); + else + break; + } + + while(str[i] != 0 && isspace(str[i])) i++; + str += i; + + return 1; +} + +// +// Regexp +// +template class CRegexpT +{ +public: + CRegexpT(const CHART * pattern = 0, int flags = 0); + CRegexpT(const CHART * pattern, int length, int flags); + void Compile(const CHART * pattern, int flags = 0); + void Compile(const CHART * pattern, int length, int flags); + +public: + MatchResult MatchExact(const CHART * tstring, CContext * pContext = 0) const; + MatchResult MatchExact(const CHART * tstring, int length, CContext * pContext = 0) const; + MatchResult Match(const CHART * tstring, int start = -1, CContext * pContext = 0) const; + MatchResult Match(const CHART * tstring, int length, int start, CContext * pContext = 0) const; + MatchResult Match(CContext * pContext) const; + CContext * PrepareMatch(const CHART * tstring, int start = -1, CContext * pContext = 0) const; + CContext * PrepareMatch(const CHART * tstring, int length, int start, CContext * pContext = 0) const; + CHART * Replace(const CHART * tstring, const CHART * replaceto, int start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const; + CHART * Replace(const CHART * tstring, int string_length, const CHART * replaceto, int to_length, int & result_length, int start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const; + int GetNamedGroupNumber(const CHART * group_name) const; + +public: + static void ReleaseString (CHART * tstring ); + static void ReleaseContext(CContext * pContext); + +public: + CBuilderT m_builder; +}; + +// +// Implementation +// +template CRegexpT :: CRegexpT(const CHART * pattern, int flags) +{ + Compile(pattern, CBufferRefT(pattern).GetSize(), flags); +} + +template CRegexpT :: CRegexpT(const CHART * pattern, int length, int flags) +{ + Compile(pattern, length, flags); +} + +template inline void CRegexpT :: Compile(const CHART * pattern, int flags) +{ + Compile(pattern, CBufferRefT(pattern).GetSize(), flags); +} + +template void CRegexpT :: Compile(const CHART * pattern, int length, int flags) +{ + m_builder.Clear(); + if(pattern != 0) m_builder.Build(CBufferRefT(pattern, length), flags); +} + +template inline MatchResult CRegexpT :: MatchExact(const CHART * tstring, CContext * pContext) const +{ + return MatchExact(tstring, CBufferRefT(tstring).GetSize(), pContext); +} + +template MatchResult CRegexpT :: MatchExact(const CHART * tstring, int length, CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + // info + int endpos = 0; + + CContext context; + if(pContext == 0) pContext = &context; + + pContext->m_stack.Restore(0); + pContext->m_capturestack.Restore(0); + pContext->m_captureindex.Restore(0); + + pContext->m_nParenZindex = 0; + pContext->m_nLastBeginPos = -1; + pContext->m_pMatchString = (void*)tstring; + pContext->m_pMatchStringLength = length; + pContext->m_nCursiveLimit = 100; + + if(m_builder.m_nFlags & RIGHTTOLEFT) + { + pContext->m_nBeginPos = length; + pContext->m_nCurrentPos = length; + endpos = 0; + } + else + { + pContext->m_nBeginPos = 0; + pContext->m_nCurrentPos = 0; + endpos = length; + } + + pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1); + pContext->m_captureindex[0] = 0; + pContext->m_capturestack.Push(0); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(-1); + + // match + if( ! m_builder.m_pTopElx->Match( pContext ) ) + return 0; + else + { + while( pContext->m_nCurrentPos != endpos ) + { + if( ! m_builder.m_pTopElx->MatchNext( pContext ) ) + return 0; + else + { + if( pContext->m_nLastBeginPos == pContext->m_nBeginPos && pContext->m_nBeginPos == pContext->m_nCurrentPos ) + return 0; + else + pContext->m_nLastBeginPos = pContext->m_nCurrentPos; + } + } + + // end pos + pContext->m_capturestack[2] = pContext->m_nCurrentPos; + + return MatchResult( pContext, m_builder.m_nMaxNumber ); + } +} + +template MatchResult CRegexpT :: Match(const CHART * tstring, int start, CContext * pContext) const +{ + return Match(tstring, CBufferRefT(tstring).GetSize(), start, pContext); +} + +template MatchResult CRegexpT :: Match(const CHART * tstring, int length, int start, CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + CContext context; + if(pContext == 0) pContext = &context; + + PrepareMatch(tstring, length, start, pContext); + + return Match( pContext ); +} + +template MatchResult CRegexpT :: Match(CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + int endpos, delta; + + if(m_builder.m_nFlags & RIGHTTOLEFT) + { + endpos = -1; + delta = -1; + } + else + { + endpos = pContext->m_pMatchStringLength + 1; + delta = 1; + } + + while(pContext->m_nCurrentPos != endpos) + { + pContext->m_captureindex.Restore(0); + pContext->m_stack .Restore(0); + pContext->m_capturestack.Restore(0); + + pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1); + pContext->m_captureindex[0] = 0; + pContext->m_capturestack.Push(0); + pContext->m_capturestack.Push(pContext->m_nCurrentPos); + pContext->m_capturestack.Push(-1); + pContext->m_capturestack.Push(-1); + + if( m_builder.m_pTopElx->Match( pContext ) ) + { + pContext->m_capturestack[2] = pContext->m_nCurrentPos; + + // zero width + if( pContext->m_capturestack[1] == pContext->m_nCurrentPos ) + { + pContext->m_nCurrentPos += delta; + } + + // save pos + pContext->m_nLastBeginPos = pContext->m_nBeginPos; + pContext->m_nBeginPos = pContext->m_nCurrentPos; + + // return + return MatchResult( pContext, m_builder.m_nMaxNumber ); + } + else + { + pContext->m_nCurrentPos += delta; + } + } + + return 0; +} + +template inline CContext * CRegexpT :: PrepareMatch(const CHART * tstring, int start, CContext * pContext) const +{ + return PrepareMatch(tstring, CBufferRefT(tstring).GetSize(), start, pContext); +} + +template CContext * CRegexpT :: PrepareMatch(const CHART * tstring, int length, int start, CContext * pContext) const +{ + if(m_builder.m_pTopElx == 0) + return 0; + + if(pContext == 0) pContext = new CContext(); + + pContext->m_nParenZindex = 0; + pContext->m_nLastBeginPos = -1; + pContext->m_pMatchString = (void*)tstring; + pContext->m_pMatchStringLength = length; + pContext->m_nCursiveLimit = 100; + + if(start < 0) + { + if(m_builder.m_nFlags & RIGHTTOLEFT) + { + pContext->m_nBeginPos = length; + pContext->m_nCurrentPos = length; + } + else + { + pContext->m_nBeginPos = 0; + pContext->m_nCurrentPos = 0; + } + } + else + { + if(start > length) start = length + ((m_builder.m_nFlags & RIGHTTOLEFT)?0:1); + + pContext->m_nBeginPos = start; + pContext->m_nCurrentPos = start; + } + + return pContext; +} + +template inline int CRegexpT :: GetNamedGroupNumber(const CHART * group_name) const +{ + return m_builder.GetNamedNumber(group_name); +} + +template CHART * CRegexpT :: Replace(const CHART * tstring, const CHART * replaceto, int start, int ntimes, MatchResult * result, CContext * pContext) const +{ + int result_length = 0; + return Replace(tstring, CBufferRefT(tstring).GetSize(), replaceto, CBufferRefT(replaceto).GetSize(), result_length, start, ntimes, result, pContext); +} + +template CHART * CRegexpT :: Replace(const CHART * tstring, int string_length, const CHART * replaceto, int to_length, int & result_length, int start, int ntimes, MatchResult * remote_result, CContext * oContext) const +{ + if(m_builder.m_pTopElx == 0) return 0; + + // --- compile replace to --- + + CBufferT compiledto; + + static const CHART rtoptn[] = { RCHART('\\'), RCHART('$' ), RCHART('('), RCHART('?'), RCHART(':'), RCHART('[' ), RCHART('$' ), RCHART('&' ), RCHART('`' ), RCHART('\''), RCHART('+'), RCHART('_' ), RCHART('\\'), RCHART('d'), RCHART(']'), RCHART('|'), RCHART('\\'), RCHART('{'), RCHART('.'), RCHART('*'), RCHART('?'), RCHART('\\'), RCHART('}'), RCHART(')' ), RCHART('\0') }; + static CRegexpT rtoreg(rtoptn); + + MatchResult local_result(0), * result = remote_result ? remote_result : & local_result; + + // prepare + CContext * pContext = rtoreg.PrepareMatch(replaceto, to_length, -1, oContext); + int lastIndex = 0, nmatch = 0; + + while( ((*result) = rtoreg.Match(pContext)).IsMatched() ) + { + int delta = result->GetStart() - lastIndex; + if( delta > 0 ) + { + compiledto.Push(lastIndex); + compiledto.Push(delta); + } + + lastIndex = result->GetStart(); + delta = 2; + + switch(replaceto[lastIndex + 1]) + { + case RCHART('$'): + compiledto.Push(lastIndex); + compiledto.Push(1); + break; + + case RCHART('&'): + case RCHART('`'): + case RCHART('\''): + case RCHART('+'): + case RCHART('_'): + compiledto.Push(-1); + compiledto.Push((int)replaceto[lastIndex + 1]); + break; + + case RCHART('{'): + delta = result->GetEnd() - result->GetStart(); + nmatch = m_builder.GetNamedNumber(CBufferRefT (replaceto + (lastIndex + 2), delta - 3)); + + if(nmatch > 0 && nmatch <= m_builder.m_nMaxNumber) + { + compiledto.Push(-2); + compiledto.Push(nmatch); + } + else + { + compiledto.Push(lastIndex); + compiledto.Push(delta); + } + break; + + default: + nmatch = 0; + for(delta=1; delta<=3; delta++) + { + CHART ch = replaceto[lastIndex + delta]; + + if(ch < RCHART('0') || ch > RCHART('9')) + break; + + nmatch = nmatch * 10 + (ch - RCHART('0')); + } + + if(nmatch > m_builder.m_nMaxNumber) + { + while(nmatch > m_builder.m_nMaxNumber) + { + nmatch /= 10; + delta --; + } + + if(nmatch == 0) + { + delta = 1; + } + } + + if(delta == 1) + { + compiledto.Push(lastIndex); + compiledto.Push(1); + } + else + { + compiledto.Push(-2); + compiledto.Push(nmatch); + } + break; + } + + lastIndex += delta; + } + + if(lastIndex < to_length) + { + compiledto.Push(lastIndex); + compiledto.Push(to_length - lastIndex); + } + + int rightleft = m_builder.m_nFlags & RIGHTTOLEFT; + + int tb = rightleft ? compiledto.GetSize() - 2 : 0; + int te = rightleft ? -2 : compiledto.GetSize(); + int ts = rightleft ? -2 : 2; + + // --- compile complete --- + + int beginpos = rightleft ? string_length : 0; + int endpos = rightleft ? 0 : string_length; + + int toIndex0 = 0; + int toIndex1 = 0; + int i, ntime; + + CBufferT buffer; + + // prepare + pContext = PrepareMatch(tstring, string_length, start, pContext); + lastIndex = beginpos; + + // Match + for(ntime = 0; ntimes < 0 || ntime < ntimes; ntime ++) + { + (*result) = Match(pContext); + + if( ! result->IsMatched() ) + break; + + // before + if( rightleft ) + { + int distance = lastIndex - result->GetEnd(); + if( distance ) + { + buffer.Push(tstring + result->GetEnd()); + buffer.Push((const CHART *)distance); + + toIndex1 -= distance; + } + lastIndex = result->GetStart(); + } + else + { + int distance = result->GetStart() - lastIndex; + if( distance ) + { + buffer.Push(tstring + lastIndex); + buffer.Push((const CHART *)distance); + + toIndex1 += distance; + } + lastIndex = result->GetEnd(); + } + + toIndex0 = toIndex1; + + // middle + for(i=tb; i!=te; i+=ts) + { + int off = compiledto[i]; + int len = compiledto[i + 1]; + + const CHART * sub = replaceto + off; + + if( off == -1 ) + { + switch(RCHART(len)) + { + case RCHART('&'): + sub = tstring + result->GetStart(); + len = result->GetEnd() - result->GetStart(); + break; + + case RCHART('`'): + sub = tstring; + len = result->GetStart(); + break; + + case RCHART('\''): + sub = tstring + result->GetEnd(); + len = string_length - result->GetEnd(); + break; + + case RCHART('+'): + for(nmatch = result->MaxGroupNumber(); nmatch >= 0; nmatch --) + { + if(result->GetGroupStart(nmatch) >= 0) break; + } + sub = tstring + result->GetGroupStart(nmatch); + len = result->GetGroupEnd(nmatch) - result->GetGroupStart(nmatch); + break; + + case RCHART('_'): + sub = tstring; + len = string_length; + break; + } + } + else if( off == -2 ) + { + sub = tstring + result->GetGroupStart(len); + len = result->GetGroupEnd(len) - result->GetGroupStart(len); + } + + buffer.Push(sub); + buffer.Push((const CHART *)len); + + toIndex1 += rightleft ? (-len) : len; + } + } + + // after + if(rightleft) + { + if(endpos < lastIndex) + { + buffer.Push(tstring + endpos); + buffer.Push((const CHART *)(lastIndex - endpos)); + } + } + else + { + if(lastIndex < endpos) + { + buffer.Push(tstring + lastIndex); + buffer.Push((const CHART *)(endpos - lastIndex)); + } + } + + if(oContext == 0) ReleaseContext(pContext); + + // join string + result_length = 0; + for(i=0; i result_string; + result_string.Prepare(result_length); + result_string.Restore(0); + + if(rightleft) + { + for(i=buffer.GetSize()-2; i>=0; i-=2) + { + result_string.Append(buffer[i], *(int*)(void*)&buffer[i+1]); + } + } + else + { + for(i=0; im_result.Append(result_length, 3); + result->m_result.Append(ntime); + + if(rightleft) + { + result->m_result.Append(result_length - toIndex1); + result->m_result.Append(result_length - toIndex0); + } + else + { + result->m_result.Append(toIndex0); + result->m_result.Append(toIndex1); + } + + return result_string.Detach(); +} + +template inline void CRegexpT :: ReleaseString(CHART * tstring) +{ + if(tstring != 0) free(tstring); +} + +template inline void CRegexpT :: ReleaseContext(CContext * pContext) +{ + if(pContext != 0) delete pContext; +} + +// +// All implementations +// +template CAlternativeElxT :: CAlternativeElxT() +{ +} + +template int CAlternativeElxT :: Match(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 1; + + // try all + for(int n = 0; n < m_elxlist.GetSize(); n++) + { + if(m_elxlist[n]->Match(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + } + + return 0; +} + +template int CAlternativeElxT :: MatchNext(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 0; + + int n = 0; + + // recall prev + pContext->m_stack.Pop(n); + + // prev + if(m_elxlist[n]->MatchNext(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + else + { + // try rest + for(n++; n < m_elxlist.GetSize(); n++) + { + if(m_elxlist[n]->Match(pContext)) + { + pContext->m_stack.Push(n); + return 1; + } + } + + return 0; + } +} + +// assertx.cpp: implementation of the CAssertElx class. +// +template CAssertElxT :: CAssertElxT(ElxInterface * pelx, int byes) +{ + m_pelx = pelx; + m_byes = byes; +} + +template int CAssertElxT :: Match(CContext * pContext) const +{ + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + int bsucc; + + // match + if( m_byes ) + bsucc = m_pelx->Match(pContext); + else + bsucc = ! m_pelx->Match(pContext); + + // status + pContext->m_stack.Restore(nsize); + pContext->m_nCurrentPos = nbegin; + + if( bsucc ) + pContext->m_stack.Push(ncsize); + else + pContext->m_capturestack.Restore(ncsize); + + return bsucc; +} + +template int CAssertElxT :: MatchNext(CContext * pContext) const +{ + int ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_capturestack.Restore(ncsize); + + return 0; +} + +// emptyelx.cpp: implementation of the CEmptyElx class. +// +template CEmptyElxT :: CEmptyElxT() +{ +} + +template int CEmptyElxT :: Match(CContext *) const +{ + return 1; +} + +template int CEmptyElxT :: MatchNext(CContext *) const +{ + return 0; +} + +// globalx.cpp: implementation of the CGlobalElx class. +// +template CGlobalElxT ::CGlobalElxT() +{ +} + +template int CGlobalElxT :: Match(CContext * pContext) const +{ + return pContext->m_nCurrentPos == pContext->m_nBeginPos; +} + +template int CGlobalElxT :: MatchNext(CContext *) const +{ + return 0; +} + +// greedelx.cpp: implementation of the CGreedyElx class. +// +template CGreedyElxT :: CGreedyElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT (pelx, nmin) +{ + m_nvart = nmax - nmin; +} + +template int CGreedyElxT :: Match(CContext * pContext) const +{ + if( ! CRepeatElxT :: MatchFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CGreedyElxT :: MatchNext(CContext * pContext) const +{ + if( MatchNextVart(pContext) ) + return 1; + + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CGreedyElxT :: MatchVart(CContext * pContext) const +{ + int n = 0; + int nbegin00 = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + + while(n < m_nvart && CRepeatElx::MatchForward(pContext)) + { + n ++; + } + + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(nsize); + pContext->m_stack.Push(pContext->m_nCurrentPos); + pContext->m_stack.Push(1); + pContext->m_stack.Push(nbegin00); + pContext->m_stack.Push(n); + + return 1; +} + +template int CGreedyElxT :: MatchNextVart(CContext * pContext) const +{ + int n, nbegin00, nsize, ncsize; + CSortedBufferT nbegin99; + pContext->m_stack.Pop(n); + pContext->m_stack.Pop(nbegin00); + pContext->m_stack.Pop(nbegin99); + pContext->m_stack.Pop(nsize); + pContext->m_stack.Pop(ncsize); + + if(n == 0) return 0; + + int n0 = n; + + if( ! CRepeatElxT::m_pelx->MatchNext(pContext) ) + { + n --; + } + + // not to re-match + else if(pContext->m_nCurrentPos == nbegin00) + { + pContext->m_stack.Restore(nsize); + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin00; + + return 0; + } + + // fix 2012-10-26, thanks to chenlx01@sohu.com + else + { + CContextShot shot(pContext); + + while(n < m_nvart && CRepeatElx::MatchForward(pContext)) + { + n ++; + } + + if(nbegin99.Find(pContext->m_nCurrentPos) >= 0) + { + shot.Restore(pContext); + n = n0; + } + else + { + nbegin99.Add(pContext->m_nCurrentPos); + } + } + + pContext->m_stack.Push(ncsize); + pContext->m_stack.Push(nsize); + pContext->m_stack.Push(nbegin99); + pContext->m_stack.Push(nbegin00); + pContext->m_stack.Push(n); + + return 1; +} + +// indepelx.cpp: implementation of the CIndependentElx class. +// +template CIndependentElxT :: CIndependentElxT(ElxInterface * pelx) +{ + m_pelx = pelx; +} + +template int CIndependentElxT :: Match(CContext * pContext) const +{ + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + + // match + int bsucc = m_pelx->Match(pContext); + + // status + pContext->m_stack.Restore(nsize); + + if( bsucc ) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(ncsize); + } + + return bsucc; +} + +template int CIndependentElxT :: MatchNext(CContext * pContext) const +{ + int nbegin = 0, ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_stack.Pop(nbegin); + + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin; + + return 0; +} + +// listelx.cpp: implementation of the CListElx class. +// +template CListElxT :: CListElxT(int brightleft) +{ + m_brightleft = brightleft; +} + +template int CListElxT :: Match(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 1; + + // prepare + int bol = m_brightleft ? m_elxlist.GetSize() : -1; + int stp = m_brightleft ? -1 : 1; + int eol = m_brightleft ? -1 : m_elxlist.GetSize(); + + // from first + int n = bol + stp; + + // match all + while(n != eol) + { + if(m_elxlist[n]->Match(pContext)) + { + n += stp; + } + else + { + n -= stp; + + while(n != bol && ! m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if(n != bol) + n += stp; + else + return 0; + } + } + + return 1; +} + +template int CListElxT :: MatchNext(CContext * pContext) const +{ + if(m_elxlist.GetSize() == 0) + return 0; + + // prepare + int bol = m_brightleft ? m_elxlist.GetSize() : -1; + int stp = m_brightleft ? -1 : 1; + int eol = m_brightleft ? -1 : m_elxlist.GetSize(); + + // from last + int n = eol - stp; + + while(n != bol && ! m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if(n != bol) + n += stp; + else + return 0; + + // match rest + while(n != eol) + { + if(m_elxlist[n]->Match(pContext)) + { + n += stp; + } + else + { + n -= stp; + + while(n != bol && ! m_elxlist[n]->MatchNext(pContext)) + n -= stp; + + if(n != bol) + n += stp; + else + return 0; + } + } + + return 1; +} + +// mresult.cpp: implementation of the MatchResult class. +// +template MatchResultT :: MatchResultT(CContext * pContext, int nMaxNumber) +{ + if(pContext != 0) + { + m_result.Prepare(nMaxNumber * 2 + 3, -1); + + // matched + m_result[0] = 1; + m_result[1] = nMaxNumber; + + for(int n = 0; n <= nMaxNumber; n++) + { + int index = pContext->m_captureindex[n]; + //if( index < 0 ) continue; + if( ! CBracketElxT::CheckCaptureIndex(index, pContext, n) ) continue; + + // check enclosed + int pos1 = pContext->m_capturestack[index + 1]; + int pos2 = pContext->m_capturestack[index + 2]; + + // info + m_result[n*2 + 2] = pos1 < pos2 ? pos1 : pos2; + m_result[n*2 + 3] = pos1 < pos2 ? pos2 : pos1; + } + } +} + +template inline int MatchResultT :: IsMatched() const +{ + return m_result.At(0, 0); +} + +template inline int MatchResultT :: MaxGroupNumber() const +{ + return m_result.At(1, 0); +} + +template inline int MatchResultT :: GetStart() const +{ + return m_result.At(2, -1); +} + +template inline int MatchResultT :: GetEnd() const +{ + return m_result.At(3, -1); +} + +template inline int MatchResultT :: GetGroupStart(int nGroupNumber) const +{ + return m_result.At(2 + nGroupNumber * 2, -1); +} + +template inline int MatchResultT :: GetGroupEnd(int nGroupNumber) const +{ + return m_result.At(2 + nGroupNumber * 2 + 1, -1); +} + +template MatchResultT & MatchResultT :: operator = (const MatchResultT & result) +{ + m_result.Restore(0); + if(result.m_result.GetSize() > 0) m_result.Append(result.m_result.GetBuffer(), result.m_result.GetSize()); + + return *this; +} + +// posselx.cpp: implementation of the CPossessiveElx class. +// +template CPossessiveElxT :: CPossessiveElxT(ElxInterface * pelx, int nmin, int nmax) : CGreedyElxT (pelx, nmin, nmax) +{ +} + +template int CPossessiveElxT :: Match(CContext * pContext) const +{ + int nbegin = pContext->m_nCurrentPos; + int nsize = pContext->m_stack.GetSize(); + int ncsize = pContext->m_capturestack.GetSize(); + int bsucc = 1; + + // match + if( ! CRepeatElxT :: MatchFixed(pContext) ) + { + bsucc = 0; + } + else + { + while( ! CGreedyElxT :: MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + { + bsucc = 0; + break; + } + } + } + + // status + pContext->m_stack.Restore(nsize); + + if( bsucc ) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(ncsize); + } + + return bsucc; +} + +template int CPossessiveElxT :: MatchNext(CContext * pContext) const +{ + int nbegin = 0, ncsize = 0; + + pContext->m_stack.Pop(ncsize); + pContext->m_stack.Pop(nbegin); + + pContext->m_capturestack.Restore(ncsize); + pContext->m_nCurrentPos = nbegin; + + return 0; +} + +// reluctx.cpp: implementation of the CReluctantElx class. +// +template CReluctantElxT :: CReluctantElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT (pelx, nmin) +{ + m_nvart = nmax - nmin; +} + +template int CReluctantElxT :: Match(CContext * pContext) const +{ + if( ! CRepeatElxT :: MatchFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CReluctantElxT :: MatchNext(CContext * pContext) const +{ + if( MatchNextVart(pContext) ) + return 1; + + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + + while( ! MatchVart(pContext) ) + { + if( ! CRepeatElxT :: MatchNextFixed(pContext) ) + return 0; + } + + return 1; +} + +template int CReluctantElxT :: MatchVart(CContext * pContext) const +{ + pContext->m_stack.Push(0); + + return 1; +} + +template int CReluctantElxT :: MatchNextVart(CContext * pContext) const +{ + int n = 0, nbegin = pContext->m_nCurrentPos; + + pContext->m_stack.Pop(n); + + if(n < m_nvart && CRepeatElxT :: m_pelx->Match(pContext)) + { + while(pContext->m_nCurrentPos == nbegin) + { + if( ! CRepeatElxT :: m_pelx->MatchNext(pContext) ) break; + } + + if(pContext->m_nCurrentPos != nbegin) + { + n ++; + + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(n); + + return 1; + } + } + + while(n > 0) + { + pContext->m_stack.Pop(nbegin); + + while( CRepeatElxT :: m_pelx->MatchNext(pContext) ) + { + if(pContext->m_nCurrentPos != nbegin) + { + pContext->m_stack.Push(nbegin); + pContext->m_stack.Push(n); + + return 1; + } + } + + n --; + } + + return 0; +} + +// repeatx.cpp: implementation of the CRepeatElx class. +// +template CRepeatElxT :: CRepeatElxT(ElxInterface * pelx, int ntimes) +{ + m_pelx = pelx; + m_nfixed = ntimes; +} + +template int CRepeatElxT :: Match(CContext * pContext) const +{ + return MatchFixed(pContext); +} + +template int CRepeatElxT :: MatchNext(CContext * pContext) const +{ + return MatchNextFixed(pContext); +} + +template int CRepeatElxT :: MatchFixed(CContext * pContext) const +{ + if(m_nfixed == 0) + return 1; + + int n = 0; + + while(n < m_nfixed) + { + if(m_pelx->Match(pContext)) + { + n ++; + } + else + { + n --; + + while(n >= 0 && ! m_pelx->MatchNext(pContext)) + n --; + + if(n >= 0) + n ++; + else + return 0; + } + } + + return 1; +} + +template int CRepeatElxT :: MatchNextFixed(CContext * pContext) const +{ + if(m_nfixed == 0) + return 0; + + // from last + int n = m_nfixed - 1; + + while(n >= 0 && ! m_pelx->MatchNext(pContext)) + n --; + + if(n >= 0) + n ++; + else + return 0; + + // match rest + while(n < m_nfixed) + { + if(m_pelx->Match(pContext)) + { + n ++; + } + else + { + n --; + + while(n >= 0 && ! m_pelx->MatchNext(pContext)) + n --; + + if(n >= 0) + n ++; + else + return 0; + } + } + + return 1; +} + +// Regexp +typedef CRegexpT CRegexpA; +typedef CRegexpT CRegexpW; + +#if defined(_UNICODE) || defined(UNICODE) + typedef CRegexpW CRegexp; +#else + typedef CRegexpA CRegexp; +#endif + +#endif//__DEELX_REGEXP__H__ diff --git a/src/Notepad3.rc b/src/Notepad3.rc index 175ac7505..f167dba2d 100644 --- a/src/Notepad3.rc +++ b/src/Notepad3.rc @@ -1356,7 +1356,7 @@ END STRINGTABLE BEGIN IDS_BACKSLASHHELP "Backslash Transformations\n\n\\a\tAlert (BEL, Ascii 7)\n\\b\tBackspace (BS, Ascii 8)\n\\f\tFormfeed (FF, Ascii 12)\n\\n\tNewline (LF, Ascii 10)\n\\r\tCarriage return (CR, Ascii 13)\n\\t\tHorizontal Tab (HT, Ascii 9)\n\\v\tVertical Tab (VT, Ascii 11)\n\\ooo\tOctal Value\n\\u####\tHexadecimal Value\n\\xhh\tHexadecimal Value\n\\\\\tBackslash" - IDS_REGEXPHELP "RegExp Syntax (Single Lines Only)\n\n.\tAny character\n^\tStart of a line\n$\tEnd of a line\n\\<\tStart of a word\n\\>\tEnd of a word\n[...]\tA set of chars ([abc]) or a range ([a-z])\n[^...]\tChars NOT in the set or range\n\\d\tAny decimal digit\n\\D\tAny non-digit char\n\\s\tAny whitespace char\n\\S\tNot a whitespace char\n\\w\tAny ""word"" char\n\\W\tAny ""non-word"" char\n\\x\tEscape character with otherwise special meaning\n\\xHH\tChar with hex code HH\n?\tMatches preceding 0 or 1 times\n*\tMatches preceding 0 or more times\n+\tMatches preceding 1 or more times\n*? or +?\tNon greedy matching of quantifiers ""?"" and ""+""\n(\tStart of a region\n)\tEnd of a region\n\\n\tRefers to a region when replacing (n is 1-9)\n" + IDS_REGEXPHELP "RegExp Syntax (Multi Lines)\n\n.\tAny character, except line-breaks\n^\tStart of a line\n$\tEnd of a line\n\\<\tStart of a word\n\\>\tEnd of a word\n[...]\tA set of chars ([abc]) or a range ([a-z])\n[^...]\tChars NOT in the set or range\n\\d\tAny decimal digit\n\\D\tAny non-digit char\n\\s\tAny whitespace char\n\\S\tNot a whitespace char\n\\w\tAny ""word"" char\n\\W\tAny ""non-word"" char\n\\x\tEscape character with otherwise special meaning\n\\xHH\tChar with hex code HH\n?\tMatches preceding 0 or 1 times\n*\tMatches preceding 0 or more times\n+\tMatches preceding 1 or more times\n*? or +?\tNon greedy matching of quantifiers ""?"" and ""+""\n(\tStart of a region\n)\tEnd of a region\n\\n\tRefers to a region when replacing (n is 1-9)\n" IDS_WILDCARDHELP "Wildcard Search\n\n*\tMatches zero or more characters.\n?\tMatches exactly one character. " END From af672d7e4990ad6bcf0a1b216b204bdd627a30e0 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Sun, 27 Nov 2016 16:55:42 +0100 Subject: [PATCH 2/2] extend RegExpr (DeelX) based "Wildcard Search" (?*) character escaping method --- src/Edit.c | 70 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/src/Edit.c b/src/Edit.c index cb7de4832..fa5e47f49 100644 --- a/src/Edit.c +++ b/src/Edit.c @@ -5344,37 +5344,53 @@ HWND EditFindReplaceDlg(HWND hwnd,LPCEDITFINDREPLACE lpefr,BOOL bReplace) #ifdef BOOKMARK_EDITION - // Wildcard search uses the regexp engine to perform a simple search with * ? as wildcards instead of more advanced and user-unfriendly regexp syntax - void EscapeWildcards( char* szFind2 , LPCEDITFINDREPLACE lpefr ) +// Wildcard search uses the regexp engine to perform a simple search with * ? as wildcards +// instead of more advanced and user-unfriendly regexp syntax +void EscapeWildcards(char* szFind2, LPCEDITFINDREPLACE lpefr) +{ + char szWildcardEscaped[512] = { '\0' }; + int iSource = 0; + int iDest = 0; + + lpefr->fuFlags |= SCFIND_REGEXP; + + while (szFind2[iSource] != '\0') { - char szWildcardEscaped[512]; - int iSource = 0; - int iDest = 0; - - lpefr->fuFlags |= SCFIND_REGEXP; - - while( szFind2[iSource] ) + char c = szFind2[iSource]; + if (c == '*') { - char c = szFind2[iSource]; - if( c == '*' ) - { - szWildcardEscaped[iDest++] = '.'; szWildcardEscaped[iDest] = '*'; - } - else if( c == '?' ) - { - szWildcardEscaped[iDest] = '.'; - } - else - { - if( c == '.' || c == '^' || c == '$' || c == '\\' || c == '[' || c == ']' || c == '+' ) szWildcardEscaped[iDest++] = '\\'; - szWildcardEscaped[iDest] = c; - } - iSource++; - iDest++; + szWildcardEscaped[iDest++] = '.'; } - szWildcardEscaped[iDest] = (char)NULL; - lstrcpynA(szFind2,szWildcardEscaped,COUNTOF(szWildcardEscaped)); + else if (c == '?') + { + c = '.'; + } + else + { + if (c == '^' || + c == '$' || + c == '(' || + c == ')' || + c == '[' || + c == ']' || + c == '{' || + c == '}' || + c == '.' || + c == '+' || + c == '|' || + c == '\\') + { + szWildcardEscaped[iDest++] = '\\'; + } + } + szWildcardEscaped[iDest++] = c; + iSource++; } + + szWildcardEscaped[iDest] = '\0'; + + lstrcpynA(szFind2, szWildcardEscaped, COUNTOF(szWildcardEscaped)); +} #endif