1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
|
# Match a string with an expression. Perl compatible regular expressions are
# recognized. Hence, special characters should be escaped. Alternatively, one
# can use the the `r/_/.test(_)` syntax for regular expressions.
# @category String
def string.match(~pattern, s) =
regexp(pattern).test(s)
end
# Extract substrings from a string. Perl compatible regular expressions are
# recognized. Hence, special characters should be escaped. Returns a list of
# (index,value). If the list does not have a pair associated to some index, it
# means that the corresponding pattern was not found. Alter natively, one can
# use the `r/_/.exec(_)` syntax for regular expressions.
# @category String
def string.extract(~pattern, s) =
regexp(pattern).exec(s)
end
# Replace all substrings matched by a pattern by another string returned by a
# function. Alternatively, one can use the `r/_/g.replace(_)` syntax for regular
# expressions.
# @category String
# @param ~pattern Pattern (regular expression) of substrings which should be replaced.
# @param f Function getting a matched substring an returning the string to replace it with.
# @param s String whose substrings should be replaced.
def string.replace(~pattern, f, s) =
regexp(flags=["g"], pattern).replace(f, s)
end
# Split a string at "separator". Perl compatible regular expressions are
# recognized. Hence, special characters should be escaped. Alternatively, one
# can use the `r/_/.split(_)` syntax for regular expressions.
# @category String
def string.split(~separator, s) =
regexp(separator).split(s)
end
# Test whether a string contains a given prefix, substring or suffix.
# @category String
# @param ~prefix Prefix to look for.
# @param ~substring Substring to look for.
# @param ~suffix Suffix to look for.
# @param s The string to look into.
def string.contains(~prefix="", ~substring="", ~suffix="", s)
ans = ref(prefix == "" and substring == "" and suffix == "")
if prefix != "" then
ans := !ans or string.sub(s, start=0, length=string.length(prefix)) == prefix
end
if suffix != "" then
suflen = string.length(suffix)
ans := !ans or string.sub(s, start=string.length(s)-suflen, length=suflen) == suffix
end
if substring != "" then
sublen = string.length(substring)
for i = 0 to string.length(s)-sublen do
ans := !ans or (string.sub(s, start=i, length=sublen) == substring)
end
end
!ans
end
# Test whether a string is a valid integer.
# @category String
def string.is_int(s)
string.match(pattern="^\\d+$", s)
end
# Convert a string to a int.
# @category String
def string.to_int(~default=0, s)
int_of_string(default=default, s)
end
# Convert a string to a float.
# @category String
def string.to_float(~default=0., s)
float_of_string(default=default, s)
end
let string.binary = ()
# Value of a positive (unsigned) integer encoded using native memory representation.
# @category String
# @param ~little_endian Whether the memory representation is little endian.
# @param s String containing the binary representation.
def string.binary.to_int(~little_endian=true, s)
ans = ref(0)
n = string.length(s)
for i = 0 to n-1 do
ans := lsl(!ans,8) + string.nth(s, if little_endian then n-1-i else i end)
end
!ans
end
# Encode a positive (unsigned) integer using native memory representation.
# @category String
# @param ~pad Minimum length in digits (pad on the left with zeros in order to reach it)
# @param ~little_endian Whether the memory representation is little endian.
# @param s String containing the binary representation.
def string.binary.of_int(~pad=0, ~little_endian=true, d)
def rec f(d, s) =
if d > 0 then
c = string.hex_of_int(pad=2, (d mod 256))
if little_endian then
f(lsr(d, 8), "#{s}\\x#{c}")
else
f(lsr(d, 8), "\\x#{c}#{s}")
end
else
s
end
end
ret = d == 0 ? "\\x00" : f(d, "")
ret = string.unescape(ret)
len = string.length(ret)
if len < pad then
ans = string.make(char_code=0, pad-len)
if little_endian then
"#{ret}#{ans}"
else
"#{ans}#{ret}"
end
else
ret
end
end
# Add a null character at the end of a string.
# @category String
# @param s String.
def string.null_terminated(s)
s ^ "\000"
end
# Generate an identifier if no identifier was provided.
# @category String
# @param ~default Name from which identifier is generated if not present.
# @param id Proposed identifier.
def string.id.default(~default, id)
null.default(id, {string.id(default)})
end
# Return a quoted copy of the given string.
# By default, the string is assumed to be `"utf8"` encoded and is escaped
# following JSON and javascript specification.
# @category String
# @param ~encoding One of: `"ascii"` or `"utf8"`. If `null`, `utf8` is tried first and `ascii` is used as a fallback if this fails.
def string.quote(~encoding=null(), s) =
def special_char(~encoding, s)
if s == "'" then
false
else
string.escape.special_char(encoding=encoding, s)
end
end
s = string.escape(special_char=special_char, encoding=encoding, s)
"\"#{s}\""
end
let string.data_uri = ()
# Encode a string using the data uri format,
# i.e. `"data:<mime>[;base64],<data>"`.
# @category String
# @param ~base64 Encode data using the base64 format
# @param ~mime Data mime type
def string.data_uri.encode(~base64=true, ~(mime:string), s) =
s = base64 ? ";base64,#{string.base64.encode(s)}" : ",#{s}"
"data:#{mime}#{s}"
end
# Decode a string using the data uri format,
# i.e. `"data:<mime>[;base64],<data>"`.
# @category String
def string.data_uri.decode(s) =
captured = r/^data:([\/\w]+)(;base64)?,(.+)$/.exec(s)
if list.assoc.mem(1, captured) and list.assoc.mem(3, captured) then
mime = list.assoc(1, captured)
data = list.assoc(3, captured)
data =
if list.assoc.mem(2, captured) then
string.base64.decode(data)
else
data
end
data.{mime=mime}
else
null()
end
end
let string.getter = ()
# Flush all values from a string getter and return
# the concatenated result. If the getter is constant,
# return the constant string. Otherwise, call the getter
# repeatedly until it returns an empty string and return
# the concatenated result
# @category String
def string.getter.flush(~separator="", gen) =
if getter.is_constant(gen) then
getter.get(gen)
else
def rec f(data) =
chunk = getter.get(gen)
if chunk == "" then string.concat(separator=separator, data) else
f([...data, chunk])
end
end
f([])
end
end
# Combine a list of string getters `[g1, ...]`
# and return a single getter `g` such that:
# `string.getter.flush(separator=s, g) = string.concat(separator=s, list.filter(fun (s) -> s != "", [string.getter.flush(g1), ...]))`
# @category String
def string.getter.concat(l) =
len = list.length(l)
pos = ref(0)
def rec f() =
if !pos == len then "" else
gen = list.nth(l, !pos)
ret = getter.get(gen)
if ret == "" or getter.is_constant(gen) then
ref.incr(pos)
end
ret == "" ? f () : ret
end
end
getter(f)
end
let string.char.ascii = ()
# All ASCII characters code
# @category String
let string.char.ascii = list.init(128, fun (c) -> c)
# All ASCII control character codes
# @category String
let string.char.ascii.control = list.init(32, fun (c) -> c)
# All ASCII printable character codes
# @category String
let string.char.ascii.printable = list.init(96, fun (c) -> c+32)
# All ASCII alphabet character codes
# @category String
let string.char.ascii.alphabet = [
# A-Z
...list.init(24, fun (c) -> c+65),
# a-z
...list.init(24, fun (c) -> c+97),
]
# All ASCII number character codes
# @category String
let string.char.ascii.number = list.init(10, fun (c) -> c+48)
# Return a random ASCII character
# @category String
def string.char.ascii.random(range=[...string.char.ascii]) =
string.char(list.nth(range, random.int(min=0, max=list.length(range)-1)))
end
|