///|
priv struct CharClass {
neg : Bool
ranges : Array[Int]
categories : Array[CharClassCategory]
} derive(Eq)
///|
priv enum CharClassCategory {
Digit
NotDigit
Space
NotSpace
Word
NotWord
} derive(Eq)
///|
fn CharClass::new(
neg~ : Bool = false,
ranges~ : Array[Int] = [],
categories~ : Array[CharClassCategory] = []
) -> CharClass {
{ neg, ranges, categories }
}
///|
fn add_single(self : CharClass, char : Char) -> Unit {
self.ranges.push(char.to_int())
self.ranges.push(char.to_int())
}
///|
fn add_range(self : CharClass, low : Char, high : Char) -> Unit {
self.ranges.push(low.to_int())
self.ranges.push(high.to_int())
}
///|
fn add_category(self : CharClass, category : CharClassCategory) -> Unit {
self.categories.push(category)
}
///|
fn contains(self : CharClass, char : Int) -> Bool {
let result = self.ranges_contains(char) || self.categories_contains(char)
if self.neg {
not(result)
} else {
result
}
}
///|
fn ranges_contains(self : CharClass, char : Int) -> Bool {
for i = 0; i < self.ranges.length(); i = i + 2 {
let low = self.ranges[i]
let high = self.ranges[i + 1]
if char >= low && char <= high {
return true
}
} else {
return false
}
}
///|
fn categories_contains(self : CharClass, char : Int) -> Bool {
for category in self.categories {
let res = match category {
Digit => is_digit(char)
NotDigit => not(is_digit(char))
Space => is_whitespace(char)
NotSpace => not(is_whitespace(char))
Word => is_word_char(char)
NotWord => not(is_word_char(char))
}
if res {
break true
}
} else {
false
}
}
///|
impl Show for CharClass with output(self, logger) -> Unit {
logger.write_char('[')
if self.neg {
logger.write_char('^')
}
for i = 0; i < self.ranges.length(); i = i + 2 {
let min = self.ranges[i]
let max = self.ranges[i + 1]
if i > 0 {
logger.write_char(',')
}
if min == max {
logger.write_string(min.to_string())
} else {
logger.write_string(min.to_string())
logger.write_char('-')
logger.write_string(max.to_string())
}
}
for category in self.categories {
match category {
Digit => logger.write_string("\\d")
NotDigit => logger.write_string("\\D")
Space => logger.write_string("\\s")
NotSpace => logger.write_string("\\S")
Word => logger.write_string("\\w")
NotWord => logger.write_string("\\W")
}
}
logger.write_char(']')
}
///|
fn is_whitespace(char : Int) -> Bool {
return char == ' '.to_int() ||
char == '\t'.to_int() ||
char == '\n'.to_int() ||
char == '\r'.to_int() ||
char == '\u000B'.to_int() ||
char == '\u000C'.to_int() ||
char == '\u0020'.to_int() ||
char == '\u00A0'.to_int() ||
char == '\u1680'.to_int() ||
char == '\u2000'.to_int() ||
char == '\u2001'.to_int() ||
char == '\u2002'.to_int() ||
char == '\u2003'.to_int() ||
char == '\u2004'.to_int() ||
char == '\u2005'.to_int() ||
char == '\u2006'.to_int() ||
char == '\u2007'.to_int() ||
char == '\u2008'.to_int() ||
char == '\u2009'.to_int() ||
char == '\u2028'.to_int() ||
char == '\u2029'.to_int()
}
///|
fn is_word_char(char : Int) -> Bool {
return (char >= 'a'.to_int() && char <= 'z'.to_int()) ||
(char >= 'A'.to_int() && char <= 'Z'.to_int()) ||
(char >= '0'.to_int() && char <= '9'.to_int()) ||
char == '_'.to_int()
}
///|
fn is_digit(char : Int) -> Bool {
return char >= '0'.to_int() && char <= '9'.to_int()
}