///|
// A malformed brace expression is literal text. Only a fully recognized
// count is subject to numeric bounds; leading zeroes are not count syntax.
fn RegexReader::repeat_number(self : RegexReader) -> Int? {
if !(self.peek() is Some(c) && digit(c)) {
return None
}
if self.peek() == Some('0') &&
self.chars.get(self.pos + 1) is Some(c) &&
digit(c) {
return None
}
let mut value = 0
while self.peek() is Some(c) && digit(c) {
value = if value > 1000 { 1001 } else { value * 10 + c.to_int() - 48 }
self.pos += 1
}
Some(value)
}
///|
fn RegexReader::repeat_bounds(
self : RegexReader,
) -> (Int, Int)? raise ParseError {
let saved = self.pos
self.pos += 1
let bounds = match self.repeat_number() {
None => None
Some(low) => {
let high = if self.peek() == Some(',') {
self.pos += 1
if self.peek() == Some('}') {
Some(-1)
} else {
self.repeat_number()
}
} else {
Some(low)
}
match high {
Some(high) if self.peek() == Some('}') => {
self.pos += 1
Some((low, high))
}
_ => None
}
}
}
match bounds {
None => self.pos = saved
Some((low, high)) =>
if low > 1000 || high > 1000 || (high >= 0 && high < low) {
raise Invalid("invalid regex repetition count")
}
}
bounds
}
///|
fn regex_repeat_valid(tree : RegexTree, available : Int) -> Bool {
match tree {
Repeat(item, low, high) => {
if high == 0 {
return true
}
let copies = if high < 0 { low } else { high }
copies <= available &&
regex_repeat_valid(
item,
if copies > 0 {
available / copies
} else {
available
},
)
}
Sequence(items) | Alternative(items) =>
items.all(item => regex_repeat_valid(item, available))
_ => true
}
}
///|
fn regex_posix_end(chars : Array[Char], start : Int) -> Int? {
for i in start..<(chars.length() - 1) {
if chars[i] == ':' && chars[i + 1] == ']' {
return Some(i)
}
}
None
}
///|
// Flag-only groups can delimit thousands of postfix operators without any
// parenthesis nesting. Bound the constructed tree before recursive compile.
fn regex_check_depth(tree : RegexTree) -> Unit raise ParseError {
let pending = [(tree, 1)]
while pending.pop() is Some((node, depth)) {
if depth > 1000 {
raise Invalid("regex tree depth limit")
}
match node {
Repeat(item, _, _) => pending.push((item, depth + 1))
Sequence(items) | Alternative(items) =>
for item in items {
pending.push((item, depth + 1))
}
_ => ()
}
}
}