Permalink
Cannot retrieve contributors at this time
/** | |
* Copyright (c) Facebook, Inc. and its affiliates. | |
* | |
* This source code is licensed under the MIT license found in the | |
* LICENSE file in the root directory of this source tree. | |
*/ | |
module Regex; | |
class Match( | |
private sliceByteOffsets: (Int, Int) ~> String, | |
private matches: Array<Int>, | |
) uses Equality { | |
fun size(): Int { | |
this.matches.size() / 2 | |
} | |
fun get(i: Int): String { | |
this.sliceByteOffsets(this.matches[i * 2], this.matches[i * 2 + 1]) | |
} | |
fun ==(other: Match): Bool { | |
this.size() == other.size() && this.toArray() == other.toArray() | |
} | |
// Mostly useful for tests | |
fun toArray(): Array<String> { | |
Array::fillBy(this.size(), i -> this[i]) | |
} | |
} | |
value class .Regex(private handle: Runtime.GCPointer) { | |
private static fun createWithoutCheck( | |
pattern: String, | |
flags: String = "", | |
): Regex { | |
options = 0x0800; // PCRE_UTF8 | |
if (flags.contains("i")) { | |
!options = options.or(0x0001); // PCRE_CASELESS | |
}; | |
if (flags.contains("m")) { | |
!options = options.or(0x0002); // PCRE_MULTILINE | |
}; | |
Regex(initialize(pattern, options)) | |
} | |
} | |
@cpp_runtime | |
native private fun initialize(pattern: String, flags: Int): Runtime.GCPointer; | |
module end; | |
module String; | |
extension class .String { | |
fun matches(regex: Regex): Bool { | |
this.match(regex).isSome() | |
} | |
fun match(regex: Regex): ?Regex.Match { | |
sliceByteOffsets = this.sliceByteOffsets; | |
matches = this.matchInternal(regex, 0); | |
if (matches.size() == 0) { | |
None() | |
} else { | |
Some(Regex.Match((start, end) ~> sliceByteOffsets(start, end), matches)) | |
} | |
} | |
fun matchAll(regex: Regex): mutable Iterator<Regex.Match> { | |
mutable MatchIterator(this.sliceByteOffsets, this.matchInternal, regex) | |
} | |
fun replaceRegex(regex: Regex, callback: Regex.Match -> String): String { | |
output = mutable Vector[]; | |
sliceByteOffsets = this.sliceByteOffsets; | |
i = 0; | |
while ({ | |
matches = this.matchInternal(regex, i); | |
if (matches.size() == 0) { | |
false | |
} else { | |
start = matches[0]; | |
end = matches[1]; | |
if (i != start) { | |
output.push(sliceByteOffsets(i, start)); | |
}; | |
output.push( | |
callback( | |
Regex.Match((start, end) ~> sliceByteOffsets(start, end), matches), | |
), | |
); | |
!i = end; | |
true | |
} | |
}) void; | |
end = Unsafe.string_utf8_size(this); | |
if (i != end) { | |
output.push(sliceByteOffsets(i, end)); | |
}; | |
output.join("") | |
} | |
fun splitRegex(regex: Regex): mutable Iterator<String> { | |
mutable SplitRegexIterator( | |
this.sliceByteOffsets, | |
() ~> Unsafe.string_utf8_size(this), | |
this.matchInternal, | |
regex, | |
) | |
} | |
@cpp_runtime | |
private native fun sliceByteOffsets(start: Int, end: Int): String; | |
@cpp_runtime | |
private native fun matchInternal(regex: Regex, index: Int): Array<Int>; | |
} | |
mutable private class MatchIterator( | |
private sliceByteOffsets: (Int, Int) ~> String, | |
private matchInternal: (Regex, Int) ~> Array<Int>, | |
private regex: Regex, | |
private mutable i: Int = 0, | |
) extends Iterator<Regex.Match> { | |
mutable fun next(): ?Regex.Match { | |
matches = this.matchInternal(this.regex, this.i); | |
sliceByteOffsets = this.sliceByteOffsets; | |
if (matches.size() == 0) { | |
None() | |
} else { | |
// We want to go to the end of the matched string to restart the search. | |
// The first two offsets are the beginning and end of this string. | |
this.!i = matches[1]; | |
Some(Regex.Match((start, end) ~> sliceByteOffsets(start, end), matches)) | |
} | |
} | |
} | |
mutable private class SplitRegexIterator( | |
private sliceByteOffsets: (Int, Int) ~> String, | |
private byteLength: () ~> Int, | |
private matchInternal: (Regex, Int) ~> Array<Int>, | |
private regex: Regex, | |
private mutable i: Int = 0, | |
) extends Iterator<String> { | |
mutable fun next(): ?String { | |
if (this.i == -1) { | |
None() | |
} else { | |
matches = this.matchInternal(this.regex, this.i); | |
i = this.i; | |
if (matches.size() == 0) { | |
this.!i = -1; | |
Some(this.sliceByteOffsets(i, this.byteLength())) | |
} else { | |
this.!i = matches[1]; | |
Some(this.sliceByteOffsets(i, matches[0])) | |
} | |
} | |
} | |
} | |
module end; |