diff --git a/runtime/src/main/kotlin/kotlin/text/regex/AbstractCharClass.kt b/runtime/src/main/kotlin/kotlin/text/regex/AbstractCharClass.kt index 45c1b4b82a8..3717c440f15 100644 --- a/runtime/src/main/kotlin/kotlin/text/regex/AbstractCharClass.kt +++ b/runtime/src/main/kotlin/kotlin/text/regex/AbstractCharClass.kt @@ -33,6 +33,10 @@ package kotlin.text.regex +import kotlin.collections.associate +import konan.worker.freeze + + /** * Unicode category (i.e. Ll, Lu). */ @@ -44,7 +48,9 @@ internal open class UnicodeCategory(protected val category: Int) : AbstractCharC * Unicode category scope (i.e IsL, IsM, ...) */ internal class UnicodeCategoryScope(category: Int) : UnicodeCategory(category) { - override fun contains(ch: Int): Boolean = alt xor (category shr ch.toChar().category.value and 1 != 0) + override fun contains(ch: Int): Boolean { + return alt xor (((category shr ch.toChar().category.value) and 1) != 0) + } } /** @@ -52,7 +58,6 @@ internal class UnicodeCategoryScope(category: Int) : UnicodeCategory(category) { * Note: this class represent a token, not node, so being constructed by lexer. */ internal abstract class AbstractCharClass : SpecialToken() { - /** * Show if the class has alternative meaning: * if the class contains character 'a' and alt == true then the class will contains all characters except 'a'. @@ -92,39 +97,53 @@ internal abstract class AbstractCharClass : SpecialToken() { open val instance: AbstractCharClass get() = this - val surrogates: AbstractCharClass by lazy { - val result = object : AbstractCharClass() { - override fun contains(ch: Int): Boolean { - val index = ch - Char.MIN_SURROGATE.toInt() - return if (index >= 0 && index < AbstractCharClass.SURROGATE_CARDINALITY) - this.altSurrogates xor this@AbstractCharClass.lowHighSurrogates.get(index) - else - false + private val surrogates_ = konan.worker.AtomicReference() + val surrogates: AbstractCharClass + get() { + surrogates_.get()?.let { + return it } - } - result.setNegative(this.altSurrogates) - return@lazy result - } + val result = object : AbstractCharClass() { + override fun contains(ch: Int): Boolean { + val index = ch - Char.MIN_SURROGATE.toInt() - - val withoutSurrogates: AbstractCharClass by lazy { - val result = object : AbstractCharClass() { - override fun contains(ch: Int): Boolean { - val index = ch - Char.MIN_SURROGATE.toInt() - - val containslHS = if (index >= 0 && index < AbstractCharClass.SURROGATE_CARDINALITY) - this.altSurrogates xor this@AbstractCharClass.lowHighSurrogates.get(index) - else - false - - return this@AbstractCharClass.contains(ch) && !containslHS + return if (index >= 0 && index < AbstractCharClass.SURROGATE_CARDINALITY) + this.altSurrogates xor this@AbstractCharClass.lowHighSurrogates.get(index) + else + false + } } + result.setNegative(this.altSurrogates) + surrogates_.compareAndSwap(null, result.freeze()) + return surrogates_.get()!! } - result.setNegative(isNegative()) - result.mayContainSupplCodepoints = mayContainSupplCodepoints - return@lazy result - } + + + private val withoutSurrogates_ = konan.worker.AtomicReference() + val withoutSurrogates: AbstractCharClass + get() { + withoutSurrogates_.get()?.let { + return it + } + val result = object : AbstractCharClass() { + override fun contains(ch: Int): Boolean { + val index = ch - Char.MIN_SURROGATE.toInt() + + val containslHS = if (index >= 0 && index < AbstractCharClass.SURROGATE_CARDINALITY) + this.altSurrogates xor this@AbstractCharClass.lowHighSurrogates.get(index) + else + false + + return this@AbstractCharClass.contains(ch) && !containslHS + } + } + result.setNegative(isNegative()) + result.mayContainSupplCodepoints = mayContainSupplCodepoints + withoutSurrogates_ .compareAndSwap(null, result.freeze()) + return withoutSurrogates_.get()!! + } + /** * Sets this CharClass to negative form, i.e. if they will add some characters and after that set this @@ -149,68 +168,114 @@ internal abstract class AbstractCharClass : SpecialToken() { } internal abstract class CachedCharClass { - private val posValue: AbstractCharClass by lazy { computeValue() } - private val negValue: AbstractCharClass by lazy { computeValue().setNegative(true) } + lateinit private var posValue: AbstractCharClass + + lateinit private var negValue: AbstractCharClass + + // Somewhat ugly init sequence, as computeValue() may depend on fields, initialized in subclass ctor. + protected fun initValues() { + posValue = computeValue() + negValue = computeValue().setNegative(true) + } fun getValue(negative: Boolean): AbstractCharClass = if (!negative) posValue else negValue protected abstract fun computeValue(): AbstractCharClass } internal class CachedDigit : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('0', '9') } internal class CachedNonDigit : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('0', '9').setNegative(true).apply { mayContainSupplCodepoints = true } } internal class CachedSpace : CachedCharClass() { + init { + initValues() + } /* 9-13 - \t\n\x0B\f\r; 32 - ' ' */ override fun computeValue(): AbstractCharClass = CharClass().add(9, 13).add(32) } internal class CachedNonSpace : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CachedSpace().getValue(negative = true).apply { mayContainSupplCodepoints = true } } internal class CachedWord : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('a', 'z').add('A', 'Z').add('0', '9').add('_') } internal class CachedNonWord : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CachedWord().getValue(negative = true).apply { mayContainSupplCodepoints = true } } internal class CachedLower : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('a', 'z') } internal class CachedUpper : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('A', 'Z') } internal class CachedASCII : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add(0x00, 0x7F) } internal class CachedAlpha : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('a', 'z').add('A', 'Z') } internal class CachedAlnum : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = (CachedAlpha().getValue(negative = false) as CharClass).add('0', '9') } internal class CachedPunct : CachedCharClass() { + init { + initValues() + } /* Punctuation !"#$%&'()*+,-./:;<=>?@ [\]^_` {|}~ */ override fun computeValue(): AbstractCharClass = CharClass().add(0x21, 0x40).add(0x5B, 0x60).add(0x7B, 0x7E) } internal class CachedGraph : CachedCharClass() { + init { + initValues() + } /* plus punctuation */ override fun computeValue(): AbstractCharClass = (CachedAlnum().getValue(negative = false) as CharClass) @@ -220,42 +285,60 @@ internal abstract class AbstractCharClass : SpecialToken() { } internal class CachedPrint : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = (CachedGraph().getValue(negative = true) as CharClass).add(0x20) } internal class CachedBlank : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add(' ').add('\t') } internal class CachedCntrl : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add(0x00, 0x1F).add(0x7F) } internal class CachedXDigit : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = CharClass().add('0', '9').add('a', 'f').add('A', 'F') } internal class CachedRange(var start: Int, var end: Int) : CachedCharClass() { + init { + initValues() + } override fun computeValue(): AbstractCharClass = - object: AbstractCharClass() { - override fun contains(ch: Int): Boolean = alt xor (ch in start..end) - }.apply { - if (end >= Char.MIN_SUPPLEMENTARY_CODE_POINT) { - mayContainSupplCodepoints = true + object: AbstractCharClass() { + override fun contains(ch: Int): Boolean = alt xor (ch in start..end) + }.apply { + if (end >= Char.MIN_SUPPLEMENTARY_CODE_POINT) { + mayContainSupplCodepoints = true + } + val minSurrogate = Char.MIN_SURROGATE.toInt() + val maxSurrogate = Char.MAX_SURROGATE.toInt() + // There is an intersection with surrogate characters. + if (end >= minSurrogate && start <= maxSurrogate && start <= end) { + val surrogatesStart = maxOf(start, minSurrogate) - minSurrogate + val surrogatesEnd = minOf(end, maxSurrogate) - minSurrogate + lowHighSurrogates.set(surrogatesStart..surrogatesEnd) + } } - val minSurrogate = Char.MIN_SURROGATE.toInt() - val maxSurrogate = Char.MAX_SURROGATE.toInt() - // There is an intersection with surrogate characters. - if (end >= minSurrogate && start <= maxSurrogate && start <= end) { - val surrogatesStart = maxOf(start, minSurrogate) - minSurrogate - val surrogatesEnd = minOf(end, maxSurrogate) - minSurrogate - lowHighSurrogates.set(surrogatesStart..surrogatesEnd) - } - } } internal class CachedSpecialsBlock : CachedCharClass() { + init { + initValues() + } public override fun computeValue(): AbstractCharClass = CharClass().add(0xFEFF, 0xFEFF).add(0xFFF0, 0xFFFD) } @@ -263,7 +346,9 @@ internal abstract class AbstractCharClass : SpecialToken() { val category: Int, val mayContainSupplCodepoints: Boolean, val containsAllSurrogates: Boolean = false) : CachedCharClass() { - + init { + initValues() + } override fun computeValue(): AbstractCharClass { val result = UnicodeCategoryScope(category) if (containsAllSurrogates) { @@ -279,7 +364,9 @@ internal abstract class AbstractCharClass : SpecialToken() { val category: Int, val mayContainSupplCodepoints: Boolean, val containsAllSurrogates: Boolean = false) : CachedCharClass() { - + init { + initValues() + } override fun computeValue(): AbstractCharClass { val result = UnicodeCategory(category) if (containsAllSurrogates) { @@ -290,206 +377,205 @@ internal abstract class AbstractCharClass : SpecialToken() { } } - companion object { //Char.MAX_SURROGATE - Char.MIN_SURROGATE + 1 const val SURROGATE_CARDINALITY = 2048 - private var classCache: MutableMap? = null - /** * Character classes. * See http://www.unicode.org/reports/tr18/, http://www.unicode.org/Public/4.1.0/ucd/Blocks.txt */ - // TODO: Make a faster implementation. - fun createClass(name: String) : CachedCharClass = - when (name) { - "Lower" -> CachedLower() - "Upper" -> CachedUpper() - "ASCII" -> CachedASCII() - "Alpha" -> CachedAlpha() - "Digit" -> CachedDigit() - "Alnum" -> CachedAlnum() - "Punct" -> CachedPunct() - "Graph" -> CachedGraph() - "Print" -> CachedPrint() - "Blank" -> CachedBlank() - "Cntrl" -> CachedCntrl() - "XDigit" -> CachedXDigit() - "Space" -> CachedSpace() - "w" -> CachedWord() - "W" -> CachedNonWord() - "s" -> CachedSpace() - "S" -> CachedNonSpace() - "d" -> CachedDigit() - "D" -> CachedNonDigit() - "BasicLatin" -> CachedRange(0x0000, 0x007F) - "Latin-1Supplement" -> CachedRange(0x0080, 0x00FF) - "LatinExtended-A" -> CachedRange(0x0100, 0x017F) - "LatinExtended-B" -> CachedRange(0x0180, 0x024F) - "IPAExtensions" -> CachedRange(0x0250, 0x02AF) - "SpacingModifierLetters" -> CachedRange(0x02B0, 0x02FF) - "CombiningDiacriticalMarks" -> CachedRange(0x0300, 0x036F) - "Greek" -> CachedRange(0x0370, 0x03FF) - "Cyrillic" -> CachedRange(0x0400, 0x04FF) - "CyrillicSupplement" -> CachedRange(0x0500, 0x052F) - "Armenian" -> CachedRange(0x0530, 0x058F) - "Hebrew" -> CachedRange(0x0590, 0x05FF) - "Arabic" -> CachedRange(0x0600, 0x06FF) - "Syriac" -> CachedRange(0x0700, 0x074F) - "ArabicSupplement" -> CachedRange(0x0750, 0x077F) - "Thaana" -> CachedRange(0x0780, 0x07BF) - "Devanagari" -> CachedRange(0x0900, 0x097F) - "Bengali" -> CachedRange(0x0980, 0x09FF) - "Gurmukhi" -> CachedRange(0x0A00, 0x0A7F) - "Gujarati" -> CachedRange(0x0A80, 0x0AFF) - "Oriya" -> CachedRange(0x0B00, 0x0B7F) - "Tamil" -> CachedRange(0x0B80, 0x0BFF) - "Telugu" -> CachedRange(0x0C00, 0x0C7F) - "Kannada" -> CachedRange(0x0C80, 0x0CFF) - "Malayalam" -> CachedRange(0x0D00, 0x0D7F) - "Sinhala" -> CachedRange(0x0D80, 0x0DFF) - "Thai" -> CachedRange(0x0E00, 0x0E7F) - "Lao" -> CachedRange(0x0E80, 0x0EFF) - "Tibetan" -> CachedRange(0x0F00, 0x0FFF) - "Myanmar" -> CachedRange(0x1000, 0x109F) - "Georgian" -> CachedRange(0x10A0, 0x10FF) - "HangulJamo" -> CachedRange(0x1100, 0x11FF) - "Ethiopic" -> CachedRange(0x1200, 0x137F) - "EthiopicSupplement" -> CachedRange(0x1380, 0x139F) - "Cherokee" -> CachedRange(0x13A0, 0x13FF) - "UnifiedCanadianAboriginalSyllabics" -> CachedRange(0x1400, 0x167F) - "Ogham" -> CachedRange(0x1680, 0x169F) - "Runic" -> CachedRange(0x16A0, 0x16FF) - "Tagalog" -> CachedRange(0x1700, 0x171F) - "Hanunoo" -> CachedRange(0x1720, 0x173F) - "Buhid" -> CachedRange(0x1740, 0x175F) - "Tagbanwa" -> CachedRange(0x1760, 0x177F) - "Khmer" -> CachedRange(0x1780, 0x17FF) - "Mongolian" -> CachedRange(0x1800, 0x18AF) - "Limbu" -> CachedRange(0x1900, 0x194F) - "TaiLe" -> CachedRange(0x1950, 0x197F) - "NewTaiLue" -> CachedRange(0x1980, 0x19DF) - "KhmerSymbols" -> CachedRange(0x19E0, 0x19FF) - "Buginese" -> CachedRange(0x1A00, 0x1A1F) - "PhoneticExtensions" -> CachedRange(0x1D00, 0x1D7F) - "PhoneticExtensionsSupplement" -> CachedRange(0x1D80, 0x1DBF) - "CombiningDiacriticalMarksSupplement" -> CachedRange(0x1DC0, 0x1DFF) - "LatinExtendedAdditional" -> CachedRange(0x1E00, 0x1EFF) - "GreekExtended" -> CachedRange(0x1F00, 0x1FFF) - "GeneralPunctuation" -> CachedRange(0x2000, 0x206F) - "SuperscriptsandSubscripts" -> CachedRange(0x2070, 0x209F) - "CurrencySymbols" -> CachedRange(0x20A0, 0x20CF) - "CombiningMarksforSymbols" -> CachedRange(0x20D0, 0x20FF) - "LetterlikeSymbols" -> CachedRange(0x2100, 0x214F) - "NumberForms" -> CachedRange(0x2150, 0x218F) - "Arrows" -> CachedRange(0x2190, 0x21FF) - "MathematicalOperators" -> CachedRange(0x2200, 0x22FF) - "MiscellaneousTechnical" -> CachedRange(0x2300, 0x23FF) - "ControlPictures" -> CachedRange(0x2400, 0x243F) - "OpticalCharacterRecognition" -> CachedRange(0x2440, 0x245F) - "EnclosedAlphanumerics" -> CachedRange(0x2460, 0x24FF) - "BoxDrawing" -> CachedRange(0x2500, 0x257F) - "BlockElements" -> CachedRange(0x2580, 0x259F) - "GeometricShapes" -> CachedRange(0x25A0, 0x25FF) - "MiscellaneousSymbols" -> CachedRange(0x2600, 0x26FF) - "Dingbats" -> CachedRange(0x2700, 0x27BF) - "MiscellaneousMathematicalSymbols-A" -> CachedRange(0x27C0, 0x27EF) - "SupplementalArrows-A" -> CachedRange(0x27F0, 0x27FF) - "BraillePatterns" -> CachedRange(0x2800, 0x28FF) - "SupplementalArrows-B" -> CachedRange(0x2900, 0x297F) - "MiscellaneousMathematicalSymbols-B" -> CachedRange(0x2980, 0x29FF) - "SupplementalMathematicalOperators" -> CachedRange(0x2A00, 0x2AFF) - "MiscellaneousSymbolsandArrows" -> CachedRange(0x2B00, 0x2BFF) - "Glagolitic" -> CachedRange(0x2C00, 0x2C5F) - "Coptic" -> CachedRange(0x2C80, 0x2CFF) - "GeorgianSupplement" -> CachedRange(0x2D00, 0x2D2F) - "Tifinagh" -> CachedRange(0x2D30, 0x2D7F) - "EthiopicExtended" -> CachedRange(0x2D80, 0x2DDF) - "SupplementalPunctuation" -> CachedRange(0x2E00, 0x2E7F) - "CJKRadicalsSupplement" -> CachedRange(0x2E80, 0x2EFF) - "KangxiRadicals" -> CachedRange(0x2F00, 0x2FDF) - "IdeographicDescriptionCharacters" -> CachedRange(0x2FF0, 0x2FFF) - "CJKSymbolsandPunctuation" -> CachedRange(0x3000, 0x303F) - "Hiragana" -> CachedRange(0x3040, 0x309F) - "Katakana" -> CachedRange(0x30A0, 0x30FF) - "Bopomofo" -> CachedRange(0x3100, 0x312F) - "HangulCompatibilityJamo" -> CachedRange(0x3130, 0x318F) - "Kanbun" -> CachedRange(0x3190, 0x319F) - "BopomofoExtended" -> CachedRange(0x31A0, 0x31BF) - "CJKStrokes" -> CachedRange(0x31C0, 0x31EF) - "KatakanaPhoneticExtensions" -> CachedRange(0x31F0, 0x31FF) - "EnclosedCJKLettersandMonths" -> CachedRange(0x3200, 0x32FF) - "CJKCompatibility" -> CachedRange(0x3300, 0x33FF) - "CJKUnifiedIdeographsExtensionA" -> CachedRange(0x3400, 0x4DB5) - "YijingHexagramSymbols" -> CachedRange(0x4DC0, 0x4DFF) - "CJKUnifiedIdeographs" -> CachedRange(0x4E00, 0x9FFF) - "YiSyllables" -> CachedRange(0xA000, 0xA48F) - "YiRadicals" -> CachedRange(0xA490, 0xA4CF) - "ModifierToneLetters" -> CachedRange(0xA700, 0xA71F) - "SylotiNagri" -> CachedRange(0xA800, 0xA82F) - "HangulSyllables" -> CachedRange(0xAC00, 0xD7A3) - "HighSurrogates" -> CachedRange(0xD800, 0xDB7F) - "HighPrivateUseSurrogates" -> CachedRange(0xDB80, 0xDBFF) - "LowSurrogates" -> CachedRange(0xDC00, 0xDFFF) - "PrivateUseArea" -> CachedRange(0xE000, 0xF8FF) - "CJKCompatibilityIdeographs" -> CachedRange(0xF900, 0xFAFF) - "AlphabeticPresentationForms" -> CachedRange(0xFB00, 0xFB4F) - "ArabicPresentationForms-A" -> CachedRange(0xFB50, 0xFDFF) - "VariationSelectors" -> CachedRange(0xFE00, 0xFE0F) - "VerticalForms" -> CachedRange(0xFE10, 0xFE1F) - "CombiningHalfMarks" -> CachedRange(0xFE20, 0xFE2F) - "CJKCompatibilityForms" -> CachedRange(0xFE30, 0xFE4F) - "SmallFormVariants" -> CachedRange(0xFE50, 0xFE6F) - "ArabicPresentationForms-B" -> CachedRange(0xFE70, 0xFEFF) - "HalfwidthandFullwidthForms" -> CachedRange(0xFF00, 0xFFEF) - "all" -> CachedRange(0x00, 0x10FFFF) - "Specials" -> CachedSpecialsBlock() - "Cn" -> CachedCategory(CharCategory.UNASSIGNED.value, true) - "IsL" -> CachedCategoryScope(0x3E, true) - "Lu" -> CachedCategory(CharCategory.UPPERCASE_LETTER.value, true) - "Ll" -> CachedCategory(CharCategory.LOWERCASE_LETTER.value, true) - "Lt" -> CachedCategory(CharCategory.TITLECASE_LETTER.value, false) - "Lm" -> CachedCategory(CharCategory.MODIFIER_LETTER.value, false) - "Lo" -> CachedCategory(CharCategory.OTHER_LETTER.value, true) - "IsM" -> CachedCategoryScope(0x1C0, true) - "Mn" -> CachedCategory(CharCategory.NON_SPACING_MARK.value, true) - "Me" -> CachedCategory(CharCategory.ENCLOSING_MARK.value, false) - "Mc" -> CachedCategory(CharCategory.COMBINING_SPACING_MARK.value, true) - "N" -> CachedCategoryScope(0xE00, true) - "Nd" -> CachedCategory(CharCategory.DECIMAL_DIGIT_NUMBER.value, true) - "Nl" -> CachedCategory(CharCategory.LETTER_NUMBER.value, true) - "No" -> CachedCategory(CharCategory.OTHER_NUMBER.value, true) - "IsZ" -> CachedCategoryScope(0x7000, false) - "Zs" -> CachedCategory(CharCategory.SPACE_SEPARATOR.value, false) - "Zl" -> CachedCategory(CharCategory.LINE_SEPARATOR.value, false) - "Zp" -> CachedCategory(CharCategory.PARAGRAPH_SEPARATOR.value, false) - "IsC" -> CachedCategoryScope(0xF0000, true, true) - "Cc" -> CachedCategory(CharCategory.CONTROL.value, false) - "Cf" -> CachedCategory(CharCategory.FORMAT.value, true) - "Co" -> CachedCategory(CharCategory.PRIVATE_USE.value, true) - "Cs" -> CachedCategory(CharCategory.SURROGATE.value, false, true) - "IsP" -> CachedCategoryScope(1 shl CharCategory.DASH_PUNCTUATION.value or - (1 shl CharCategory.START_PUNCTUATION.value) or - (1 shl CharCategory.END_PUNCTUATION.value) or - (1 shl CharCategory.CONNECTOR_PUNCTUATION.value) or - (1 shl CharCategory.OTHER_PUNCTUATION.value) or - (1 shl CharCategory.INITIAL_QUOTE_PUNCTUATION.value) or - (1 shl CharCategory.FINAL_QUOTE_PUNCTUATION.value), true) - "Pd" -> CachedCategory(CharCategory.DASH_PUNCTUATION.value, false) - "Ps" -> CachedCategory(CharCategory.START_PUNCTUATION.value, false) - "Pe" -> CachedCategory(CharCategory.END_PUNCTUATION.value, false) - "Pc" -> CachedCategory(CharCategory.CONNECTOR_PUNCTUATION.value, false) - "Po" -> CachedCategory(CharCategory.OTHER_PUNCTUATION.value, true) - "IsS" -> CachedCategoryScope(0x7E000000, true) - "Sm" -> CachedCategory(CharCategory.MATH_SYMBOL.value, true) - "Sc" -> CachedCategory(CharCategory.CURRENCY_SYMBOL.value, false) - "Sk" -> CachedCategory(CharCategory.MODIFIER_SYMBOL.value, false) - "So" -> CachedCategory(CharCategory.OTHER_SYMBOL.value, true) - "Pi" -> CachedCategory(CharCategory.INITIAL_QUOTE_PUNCTUATION.value, false) - "Pf" -> CachedCategory(CharCategory.FINAL_QUOTE_PUNCTUATION.value, false) - else -> throw PatternSyntaxException("No such character class") - } + enum class CharClasses(val regexName : String, val factory: () -> CachedCharClass) { + LOWER("Lower", ::CachedLower), + UPPER("Upper", ::CachedUpper), + ASCII("ASCII", ::CachedASCII), + ALPHA("Alpha", ::CachedAlpha), + DIGIT("Digit", ::CachedDigit), + ALNUM("Alnum", :: CachedAlnum), + PUNCT("Punct", ::CachedPunct), + GRAPH("Graph", ::CachedGraph), + PRINT("Print", ::CachedPrint), + BLANK("Blank", ::CachedBlank), + CNTRL("Cntrl", ::CachedCntrl), + XDIGIT("XDigit", ::CachedXDigit), + SPACE("Space", ::CachedSpace), + WORD("w", ::CachedWord), + NON_WORD("W", ::CachedNonWord), + SPACE_SHORT("s", ::CachedSpace), + NON_SPACE("S", ::CachedNonSpace), + DIGIT_SHORT("d", ::CachedDigit), + NON_DIGIT("D", ::CachedNonDigit), + BASIC_LATIN("BasicLatin", { CachedRange(0x0000, 0x007F) }), + LATIN1_SUPPLEMENT("Latin-1Supplement", { CachedRange(0x0080, 0x00FF) }), + LATIN_EXTENDED_A("LatinExtended-A", { CachedRange(0x0100, 0x017F) }), + LATIN_EXTENDED_B("LatinExtended-B", { CachedRange(0x0180, 0x024F) }), + IPA_EXTENSIONS("IPAExtensions", { CachedRange(0x0250, 0x02AF) }), + SPACING_MODIFIER_LETTERS("SpacingModifierLetters", { CachedRange(0x02B0, 0x02FF) }), + COMBINING_DIACRITICAL_MARKS("CombiningDiacriticalMarks", { CachedRange(0x0300, 0x036F) }), + GREEK("Greek", { CachedRange(0x0370, 0x03FF) }), + CYRILLIC("Cyrillic", { CachedRange(0x0400, 0x04FF) }), + CYRILLIC_SUPPLEMENT("CyrillicSupplement", { CachedRange(0x0500, 0x052F) }), + ARMENIAN("Armenian", { CachedRange(0x0530, 0x058F) }), + HEBREW("Hebrew", { CachedRange(0x0590, 0x05FF) }), + ARABIC("Arabic", { CachedRange(0x0600, 0x06FF) }), + SYRIAC("Syriac", { CachedRange(0x0700, 0x074F) }), + ARABICSUPPLEMENT("ArabicSupplement", { CachedRange(0x0750, 0x077F) }), + THAANA("Thaana", { CachedRange(0x0780, 0x07BF) }), + DEVANAGARI("Devanagari", { CachedRange(0x0900, 0x097F) }), + BENGALI("Bengali", { CachedRange(0x0980, 0x09FF) }), + GURMUKHI("Gurmukhi", { CachedRange(0x0A00, 0x0A7F) }), + GUJARATI("Gujarati", { CachedRange(0x0A80, 0x0AFF) }), + ORIYA("Oriya", { CachedRange(0x0B00, 0x0B7F) }), + TAMIL("Tamil", { CachedRange(0x0B80, 0x0BFF) }), + TELUGU("Telugu", { CachedRange(0x0C00, 0x0C7F) }), + KANNADA("Kannada", { CachedRange(0x0C80, 0x0CFF) }), + MALAYALAM("Malayalam", { CachedRange(0x0D00, 0x0D7F) }), + SINHALA("Sinhala", { CachedRange(0x0D80, 0x0DFF) }), + THAI("Thai", { CachedRange(0x0E00, 0x0E7F) }), + LAO("Lao", { CachedRange(0x0E80, 0x0EFF) }), + TIBETAN("Tibetan", { CachedRange(0x0F00, 0x0FFF) }), + MYANMAR("Myanmar", { CachedRange(0x1000, 0x109F) }), + GEORGIAN("Georgian", { CachedRange(0x10A0, 0x10FF) }), + HANGULJAMO("HangulJamo", { CachedRange(0x1100, 0x11FF) }), + ETHIOPIC("Ethiopic", { CachedRange(0x1200, 0x137F) }), + ETHIOPICSUPPLEMENT("EthiopicSupplement", { CachedRange(0x1380, 0x139F) }), + CHEROKEE("Cherokee", { CachedRange(0x13A0, 0x13FF) }), + UNIFIEDCANADIANABORIGINALSYLLABICS("UnifiedCanadianAboriginalSyllabics", { CachedRange(0x1400, 0x167F) }), + OGHAM("Ogham", { CachedRange(0x1680, 0x169F) }), + RUNIC("Runic", { CachedRange(0x16A0, 0x16FF) }), + TAGALOG("Tagalog", { CachedRange(0x1700, 0x171F) }), + HANUNOO("Hanunoo", { CachedRange(0x1720, 0x173F) }), + BUHID("Buhid", { CachedRange(0x1740, 0x175F) }), + TAGBANWA("Tagbanwa", { CachedRange(0x1760, 0x177F) }), + KHMER("Khmer", { CachedRange(0x1780, 0x17FF) }), + MONGOLIAN("Mongolian", { CachedRange(0x1800, 0x18AF) }), + LIMBU("Limbu", { CachedRange(0x1900, 0x194F) }), + TAILE("TaiLe", { CachedRange(0x1950, 0x197F) }), + NEWTAILUE("NewTaiLue", { CachedRange(0x1980, 0x19DF) }), + KHMERSYMBOLS("KhmerSymbols", { CachedRange(0x19E0, 0x19FF) }), + BUGINESE("Buginese", { CachedRange(0x1A00, 0x1A1F) }), + PHONETICEXTENSIONS("PhoneticExtensions", { CachedRange(0x1D00, 0x1D7F) }), + PHONETICEXTENSIONSSUPPLEMENT("PhoneticExtensionsSupplement", { CachedRange(0x1D80, 0x1DBF) }), + COMBININGDIACRITICALMARKSSUPPLEMENT("CombiningDiacriticalMarksSupplement", { CachedRange(0x1DC0, 0x1DFF) }), + LATINEXTENDEDADDITIONAL("LatinExtendedAdditional", { CachedRange(0x1E00, 0x1EFF) }), + GREEKEXTENDED("GreekExtended", { CachedRange(0x1F00, 0x1FFF) }), + GENERALPUNCTUATION("GeneralPunctuation", { CachedRange(0x2000, 0x206F) }), + SUPERSCRIPTSANDSUBSCRIPTS("SuperscriptsandSubscripts", { CachedRange(0x2070, 0x209F) }), + CURRENCYSYMBOLS("CurrencySymbols", { CachedRange(0x20A0, 0x20CF) }), + COMBININGMARKSFORSYMBOLS("CombiningMarksforSymbols", { CachedRange(0x20D0, 0x20FF) }), + LETTERLIKESYMBOLS("LetterlikeSymbols", { CachedRange(0x2100, 0x214F) }), + NUMBERFORMS("NumberForms", { CachedRange(0x2150, 0x218F) }), + ARROWS("Arrows", { CachedRange(0x2190, 0x21FF) }), + MATHEMATICALOPERATORS("MathematicalOperators", { CachedRange(0x2200, 0x22FF) }), + MISCELLANEOUSTECHNICAL("MiscellaneousTechnical", { CachedRange(0x2300, 0x23FF) }), + CONTROLPICTURES("ControlPictures", { CachedRange(0x2400, 0x243F) }), + OPTICALCHARACTERRECOGNITION("OpticalCharacterRecognition", { CachedRange(0x2440, 0x245F) }), + ENCLOSEDALPHANUMERICS("EnclosedAlphanumerics", { CachedRange(0x2460, 0x24FF) }), + BOXDRAWING("BoxDrawing", { CachedRange(0x2500, 0x257F) }), + BLOCKELEMENTS("BlockElements", { CachedRange(0x2580, 0x259F) }), + GEOMETRICSHAPES("GeometricShapes", { CachedRange(0x25A0, 0x25FF) }), + MISCELLANEOUSSYMBOLS("MiscellaneousSymbols", { CachedRange(0x2600, 0x26FF) }), + DINGBATS("Dingbats", { CachedRange(0x2700, 0x27BF) }), + MISCELLANEOUSMATHEMATICALSYMBOLS_A("MiscellaneousMathematicalSymbols-A", { CachedRange(0x27C0, 0x27EF) }), + SUPPLEMENTALARROWS_A("SupplementalArrows-A", { CachedRange(0x27F0, 0x27FF) }), + BRAILLEPATTERNS("BraillePatterns", { CachedRange(0x2800, 0x28FF) }), + SUPPLEMENTALARROWS_B("SupplementalArrows-B", { CachedRange(0x2900, 0x297F) }), + MISCELLANEOUSMATHEMATICALSYMBOLS_B("MiscellaneousMathematicalSymbols-B", { CachedRange(0x2980, 0x29FF) }), + SUPPLEMENTALMATHEMATICALOPERATORS("SupplementalMathematicalOperators", { CachedRange(0x2A00, 0x2AFF) }), + MISCELLANEOUSSYMBOLSANDARROWS("MiscellaneousSymbolsandArrows", { CachedRange(0x2B00, 0x2BFF) }), + GLAGOLITIC("Glagolitic", { CachedRange(0x2C00, 0x2C5F) }), + COPTIC("Coptic", { CachedRange(0x2C80, 0x2CFF) }), + GEORGIANSUPPLEMENT("GeorgianSupplement", { CachedRange(0x2D00, 0x2D2F) }), + TIFINAGH("Tifinagh", { CachedRange(0x2D30, 0x2D7F) }), + ETHIOPICEXTENDED("EthiopicExtended", { CachedRange(0x2D80, 0x2DDF) }), + SUPPLEMENTALPUNCTUATION("SupplementalPunctuation", { CachedRange(0x2E00, 0x2E7F) }), + CJKRADICALSSUPPLEMENT("CJKRadicalsSupplement", { CachedRange(0x2E80, 0x2EFF) }), + KANGXIRADICALS("KangxiRadicals", { CachedRange(0x2F00, 0x2FDF) }), + IDEOGRAPHICDESCRIPTIONCHARACTERS("IdeographicDescriptionCharacters", { CachedRange(0x2FF0, 0x2FFF) }), + CJKSYMBOLSANDPUNCTUATION("CJKSymbolsandPunctuation", { CachedRange(0x3000, 0x303F) }), + HIRAGANA("Hiragana", { CachedRange(0x3040, 0x309F) }), + KATAKANA("Katakana", { CachedRange(0x30A0, 0x30FF) }), + BOPOMOFO("Bopomofo", { CachedRange(0x3100, 0x312F) }), + HANGULCOMPATIBILITYJAMO("HangulCompatibilityJamo", { CachedRange(0x3130, 0x318F) }), + KANBUN("Kanbun", { CachedRange(0x3190, 0x319F) }), + BOPOMOFOEXTENDED("BopomofoExtended", { CachedRange(0x31A0, 0x31BF) }), + CJKSTROKES("CJKStrokes", { CachedRange(0x31C0, 0x31EF) }), + KATAKANAPHONETICEXTENSIONS("KatakanaPhoneticExtensions", { CachedRange(0x31F0, 0x31FF) }), + ENCLOSEDCJKLETTERSANDMONTHS("EnclosedCJKLettersandMonths", { CachedRange(0x3200, 0x32FF) }), + CJKCOMPATIBILITY("CJKCompatibility", { CachedRange(0x3300, 0x33FF) }), + CJKUNIFIEDIDEOGRAPHSEXTENSIONA("CJKUnifiedIdeographsExtensionA", { CachedRange(0x3400, 0x4DB5) }), + YIJINGHEXAGRAMSYMBOLS("YijingHexagramSymbols", { CachedRange(0x4DC0, 0x4DFF) }), + CJKUNIFIEDIDEOGRAPHS("CJKUnifiedIdeographs", { CachedRange(0x4E00, 0x9FFF) }), + YISYLLABLES("YiSyllables", { CachedRange(0xA000, 0xA48F) }), + YIRADICALS("YiRadicals", { CachedRange(0xA490, 0xA4CF) }), + MODIFIERTONELETTERS("ModifierToneLetters", { CachedRange(0xA700, 0xA71F) }), + SYLOTINAGRI("SylotiNagri", { CachedRange(0xA800, 0xA82F) }), + HANGULSYLLABLES("HangulSyllables", { CachedRange(0xAC00, 0xD7A3) }), + HIGHSURROGATES("HighSurrogates", { CachedRange(0xD800, 0xDB7F) }), + HIGHPRIVATEUSESURROGATES("HighPrivateUseSurrogates", { CachedRange(0xDB80, 0xDBFF) }), + LOWSURROGATES("LowSurrogates", { CachedRange(0xDC00, 0xDFFF) }), + PRIVATEUSEAREA("PrivateUseArea", { CachedRange(0xE000, 0xF8FF) }), + CJKCOMPATIBILITYIDEOGRAPHS("CJKCompatibilityIdeographs", { CachedRange(0xF900, 0xFAFF) }), + ALPHABETICPRESENTATIONFORMS("AlphabeticPresentationForms", { CachedRange(0xFB00, 0xFB4F) }), + ARABICPRESENTATIONFORMS_A("ArabicPresentationForms-A", { CachedRange(0xFB50, 0xFDFF) }), + VARIATIONSELECTORS("VariationSelectors", { CachedRange(0xFE00, 0xFE0F) }), + VERTICALFORMS("VerticalForms", { CachedRange(0xFE10, 0xFE1F) }), + COMBININGHALFMARKS("CombiningHalfMarks", { CachedRange(0xFE20, 0xFE2F) }), + CJKCOMPATIBILITYFORMS("CJKCompatibilityForms", { CachedRange(0xFE30, 0xFE4F) }), + SMALLFORMVARIANTS("SmallFormVariants", { CachedRange(0xFE50, 0xFE6F) }), + ARABICPRESENTATIONFORMS_B("ArabicPresentationForms-B", { CachedRange(0xFE70, 0xFEFF) }), + HALFWIDTHANDFULLWIDTHFORMS("HalfwidthandFullwidthForms", { CachedRange(0xFF00, 0xFFEF) }), + ALL("all", { CachedRange(0x00, 0x10FFFF) }), + SPECIALS("Specials", ::CachedSpecialsBlock), + CN("Cn", { CachedCategory(CharCategory.UNASSIGNED.value, true) }), + ISL("IsL", { CachedCategoryScope(0x3E, true) }), + LU("Lu", { CachedCategory(CharCategory.UPPERCASE_LETTER.value, true) }), + LL("Ll", { CachedCategory(CharCategory.LOWERCASE_LETTER.value, true) }), + LT("Lt", { CachedCategory(CharCategory.TITLECASE_LETTER.value, false) }), + LM("Lm", { CachedCategory(CharCategory.MODIFIER_LETTER.value, false) }), + LO("Lo", { CachedCategory(CharCategory.OTHER_LETTER.value, true) }), + ISM("IsM", { CachedCategoryScope(0x1C0, true) }), + MN("Mn", { CachedCategory(CharCategory.NON_SPACING_MARK.value, true) }), + ME("Me", { CachedCategory(CharCategory.ENCLOSING_MARK.value, false) }), + MC("Mc", { CachedCategory(CharCategory.COMBINING_SPACING_MARK.value, true) }), + N("N", { CachedCategoryScope(0xE00, true) }), + ND("Nd", { CachedCategory(CharCategory.DECIMAL_DIGIT_NUMBER.value, true) }), + NL("Nl", { CachedCategory(CharCategory.LETTER_NUMBER.value, true) }), + NO("No", { CachedCategory(CharCategory.OTHER_NUMBER.value, true) }), + ISZ("IsZ", { CachedCategoryScope(0x7000, false) }), + ZS("Zs", { CachedCategory(CharCategory.SPACE_SEPARATOR.value, false) }), + ZL("Zl", { CachedCategory(CharCategory.LINE_SEPARATOR.value, false) }), + ZP("Zp", { CachedCategory(CharCategory.PARAGRAPH_SEPARATOR.value, false) }), + ISC("IsC", { CachedCategoryScope(0xF0000, true, true) }), + CC("Cc", { CachedCategory(CharCategory.CONTROL.value, false) }), + CF("Cf", { CachedCategory(CharCategory.FORMAT.value, true) }), + CO("Co", { CachedCategory(CharCategory.PRIVATE_USE.value, true) }), + CS("Cs", { CachedCategory(CharCategory.SURROGATE.value, false, true) }), + ISP("IsP", { CachedCategoryScope((1 shl CharCategory.DASH_PUNCTUATION.value) + or (1 shl CharCategory.START_PUNCTUATION.value) + or (1 shl CharCategory.END_PUNCTUATION.value) + or (1 shl CharCategory.CONNECTOR_PUNCTUATION.value) + or (1 shl CharCategory.OTHER_PUNCTUATION.value) + or (1 shl CharCategory.INITIAL_QUOTE_PUNCTUATION.value) + or (1 shl CharCategory.FINAL_QUOTE_PUNCTUATION.value), true) }), + PD("Pd", { CachedCategory(CharCategory.DASH_PUNCTUATION.value, false) }), + PS("Ps", { CachedCategory(CharCategory.START_PUNCTUATION.value, false) }), + PE("Pe", { CachedCategory(CharCategory.END_PUNCTUATION.value, false) }), + PC("Pc", { CachedCategory(CharCategory.CONNECTOR_PUNCTUATION.value, false) }), + PO("Po", { CachedCategory(CharCategory.OTHER_PUNCTUATION.value, true) }), + ISS("IsS", { CachedCategoryScope(0x7E000000, true) }), + SM("Sm", { CachedCategory(CharCategory.MATH_SYMBOL.value, true) }), + SC("Sc", { CachedCategory(CharCategory.CURRENCY_SYMBOL.value, false) }), + SK("Sk", { CachedCategory(CharCategory.MODIFIER_SYMBOL.value, false) }), + SO("So", { CachedCategory(CharCategory.OTHER_SYMBOL.value, true) }), + PI("Pi", { CachedCategory(CharCategory.INITIAL_QUOTE_PUNCTUATION.value, false) }), + PF("Pf", { CachedCategory(CharCategory.FINAL_QUOTE_PUNCTUATION.value, false) }) + } + + private val classCache = Array>(CharClasses.values().size, { + konan.worker.AtomicReference() + }) + private val classCacheMap = CharClasses.values().associate { it -> it.regexName to it } fun intersects(ch1: Int, ch2: Int): Boolean = ch1 == ch2 fun intersects(cc: AbstractCharClass, ch: Int): Boolean = cc.contains(ch) @@ -502,15 +588,10 @@ internal abstract class AbstractCharClass : SpecialToken() { } fun getPredefinedClass(name: String, negative: Boolean): AbstractCharClass { - var cache = classCache - if (cache == null) { - cache = mutableMapOf() - classCache = cache - } - var cachedClass = cache[name] - if (cachedClass == null) { - cachedClass = createClass(name) - cache[name] = cachedClass + val charClass = classCacheMap[name] ?: throw PatternSyntaxException("No such character class") + val cachedClass = classCache[charClass.ordinal].get() ?: run { + classCache[charClass.ordinal].compareAndSwap(null, charClass.factory().freeze()) + classCache[charClass.ordinal].get()!! } return cachedClass.getValue(negative) } diff --git a/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt b/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt index b4020caf941..d41aa41ebeb 100644 --- a/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt +++ b/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt @@ -772,15 +772,15 @@ internal class Lexer(val patternString: String, flags: Int) { * to description at http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf * "3.12 Conjoining Jamo Behavior" */ - val SBase = 0xAC00 - val LBase = 0x1100 - val VBase = 0x1161 - val TBase = 0x11A7 - val SCount = 11172 - val LCount = 19 - val VCount = 21 - val TCount = 28 - val NCount = 588 + const val SBase = 0xAC00 + const val LBase = 0x1100 + const val VBase = 0x1161 + const val TBase = 0x11A7 + const val SCount = 11172 + const val LCount = 19 + const val VCount = 21 + const val TCount = 28 + const val NCount = 588 // Access to the decomposition tables. ========================================================================= /** Gets canonical class for given codepoint from decomposition mappings table. */ diff --git a/runtime/src/main/kotlin/kotlin/text/regex/sets/AbstractSet.kt b/runtime/src/main/kotlin/kotlin/text/regex/sets/AbstractSet.kt index 9eed5fde586..4542137e459 100644 --- a/runtime/src/main/kotlin/kotlin/text/regex/sets/AbstractSet.kt +++ b/runtime/src/main/kotlin/kotlin/text/regex/sets/AbstractSet.kt @@ -63,6 +63,7 @@ internal abstract class AbstractSet(val type: Int = 0) { override fun hasConsumed(matchResult: MatchResultImpl): Boolean = throw AssertionError("This method is not expected to be called.") override fun processSecondPassInternal(): AbstractSet = this + override fun processSecondPass(): AbstractSet = this } } @@ -151,7 +152,7 @@ internal abstract class AbstractSet(val type: Int = 0) { /** * This method performs the second pass without checking if it's already performed or not. */ - open fun processSecondPassInternal(): AbstractSet { + protected open fun processSecondPassInternal(): AbstractSet { if (!next.secondPassVisited) { this.next = next.processSecondPass() }