end
end
- # Justify a self in a space of `length`
+ # Justify `self` in a space of `length`
#
# `left` is the space ratio on the left side.
# * 0.0 for left-justified (no space at the left)
# * 1.0 for right-justified (all spaces at the left)
# * 0.5 for centered (half the spaces at the left)
#
+ # `char`, or `' '` by default, is repeated to pad the empty space.
+ #
# Examples
#
# assert "hello".justify(10, 0.0) == "hello "
# assert "hello".justify(10, 1.0) == " hello"
# assert "hello".justify(10, 0.5) == " hello "
+ # assert "hello".justify(10, 0.5, '.') == "..hello..."
#
# If `length` is not enough, `self` is returned as is.
#
# REQUIRE: `left >= 0.0 and left <= 1.0`
# ENSURE: `self.length <= length implies result.length == length`
# ENSURE: `self.length >= length implies result == self`
- fun justify(length: Int, left: Float): String
+ fun justify(length: Int, left: Float, char: nullable Char): String
do
+ var pad = (char or else ' ').to_s
var diff = length - self.length
if diff <= 0 then return to_s
assert left >= 0.0 and left <= 1.0
var before = (diff.to_f * left).to_i
- return " " * before + self + " " * (diff-before)
+ return pad * before + self + pad * (diff-before)
end
# Mangle a string to be a unique string only made of alphanumeric characters and underscores.
return res.to_s
end
- # Escape " \ ' and non printable characters using the rules of literal C strings and characters
+ # Escape `"` `\` `'`, trigraphs and non printable characters using the rules of literal C strings and characters
#
- # assert "abAB12<>&".escape_to_c == "abAB12<>&"
+ # assert "abAB12<>&".escape_to_c == "abAB12<>&"
# assert "\n\"'\\".escape_to_c == "\\n\\\"\\'\\\\"
+ # assert "allo???!".escape_to_c == "allo??\\?!"
+ # assert "??=??/??'??(??)".escape_to_c == "?\\?=?\\?/??\\'?\\?(?\\?)"
+ # assert "??!??<??>??-".escape_to_c == "?\\?!?\\?<?\\?>?\\?-"
#
# Most non-printable characters (bellow ASCII 32) are escaped to an octal form `\nnn`.
# Three digits are always used to avoid following digits to be interpreted as an element
b.append("\\\'")
else if c == '\\' then
b.append("\\\\")
+ else if c == '?' then
+ # Escape if it is the last question mark of a ANSI C trigraph.
+ var j = i + 1
+ if j < length then
+ var next = chars[j]
+ # We ignore `??'` because it will be escaped as `??\'`.
+ if
+ next == '!' or
+ next == '(' or
+ next == ')' or
+ next == '-' or
+ next == '/' or
+ next == '<' or
+ next == '=' or
+ next == '>'
+ then b.add('\\')
+ end
+ b.add('?')
else if c.code_point < 32 then
b.add('\\')
- var oct = c.code_point.to_base(8, false)
+ var oct = c.code_point.to_base(8)
# Force 3 octal digits since it is the
# maximum allowed in the C specification
if oct.length == 1 then
# The result might no be legal in C but be used in other languages
#
# assert "ab|\{\}".escape_more_to_c("|\{\}") == "ab\\|\\\{\\\}"
+ # assert "allo???!".escape_more_to_c("") == "allo??\\?!"
fun escape_more_to_c(chars: String): String
do
var b = new Buffer
b.add('\\')
b.add(c)
else if c.code_point < 32 or c == ';' or c == '|' or c == '\\' or c == '=' then
- b.append("?{c.code_point.to_base(16, false)}")
+ b.append("?{c.code_point.to_base(16)}")
else
b.add(c)
end
end
end
+ # Packs the content of a string in packs of `ln` chars.
+ # This variant ensures that only the last element might be smaller than `ln`
+ #
+ # ~~~nit
+ # var s = "abcdefghijklmnopqrstuvwxyz"
+ # assert s.pack_l(4) == ["abcd","efgh","ijkl","mnop","qrst","uvwx","yz"]
+ # ~~~
+ fun pack_l(ln: Int): Array[Text] do
+ var st = 0
+ var retarr = new Array[Text].with_capacity(length / ln + length % ln)
+ while st < length do
+ retarr.add(substring(st, ln))
+ st += ln
+ end
+ return retarr
+ end
+
+ # Packs the content of a string in packs of `ln` chars.
+ # This variant ensures that only the first element might be smaller than `ln`
+ #
+ # ~~~nit
+ # var s = "abcdefghijklmnopqrstuvwxyz"
+ # assert s.pack_r(4) == ["ab","cdef","ghij","klmn","opqr","stuv","wxyz"]
+ # ~~~
+ fun pack_r(ln: Int): Array[Text] do
+ var st = length
+ var retarr = new Array[Text].with_capacity(length / ln + length % ln)
+ while st >= 0 do
+ retarr.add(substring(st - ln, ln))
+ st -= ln
+ end
+ return retarr.reversed
+ end
end
# All kinds of array-based text representations.
# In Buffers, the internal sequence of character is mutable
# Thus, `chars` can be used to modify the buffer.
redef fun chars: Sequence[Char] is abstract
+
+ # Appends `length` chars from `s` starting at index `from`
+ #
+ # ~~~nit
+ # var b = new Buffer
+ # b.append_substring("abcde", 1, 2)
+ # assert b == "bc"
+ # b.append_substring("vwxyz", 2, 3)
+ # assert b == "bcxyz"
+ # b.append_substring("ABCDE", 4, 300)
+ # assert b == "bcxyzE"
+ # b.append_substring("VWXYZ", 400, 1)
+ # assert b == "bcxyzE"
+ # ~~~
+ fun append_substring(s: Text, from, length: Int) do
+ if from < 0 then
+ length += from
+ from = 0
+ end
+ var ln = s.length
+ if (length + from) > ln then length = ln - from
+ if length <= 0 then return
+ append_substring_impl(s, from, length)
+ end
+
+ # Unsafe version of `append_substring` for performance
+ #
+ # NOTE: Use only if sure about `from` and `length`, no checks
+ # or bound recalculation is done
+ fun append_substring_impl(s: Text, from, length: Int) do
+ var pos = from
+ for i in [0 .. length[ do
+ self.add s[pos]
+ pos += 1
+ end
+ end
end
# View for chars on Buffer objects, extends Sequence
# Returns a string describing error number
fun strerror: String do return strerror_ext.to_s
- # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
+ # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if negative).
# assume < to_c max const of char
- private fun fill_buffer(s: Buffer, base: Int, signed: Bool)
+ private fun fill_buffer(s: Buffer, base: Int)
do
var n: Int
# Sign
snprintf(nstr, strlen, "%ld", self);
`}
- # return displayable int in base base and signed
- fun to_base(base: Int, signed: Bool): String is abstract
+ # String representation of `self` in the given `base`
+ #
+ # ~~~
+ # assert 15.to_base(10) == "15"
+ # assert 15.to_base(16) == "f"
+ # assert 15.to_base(2) == "1111"
+ # assert (-10).to_base(3) == "-101"
+ # ~~~
+ fun to_base(base: Int): String
+ do
+ var l = digit_count(base)
+ var s = new Buffer
+ s.enlarge(l)
+ for x in [0..l[ do s.add(' ')
+ fill_buffer(s, base)
+ return s.to_s
+ end
+
# return displayable int in hexadecimal
#
# assert 1.to_hex == "1"
# assert (-255).to_hex == "-ff"
- fun to_hex: String do return to_base(16,false)
+ fun to_hex: String do return to_base(16)
end
redef class Float
return cp >= 0xD800 and cp <= 0xDFFF
end
+ # Is `self` a UTF-16 high surrogate ?
+ fun is_hi_surrogate: Bool do
+ var cp = code_point
+ return cp >= 0xD800 and cp <= 0xDBFF
+ end
+
+ # Is `self` a UTF-16 low surrogate ?
+ fun is_lo_surrogate: Bool do
+ var cp = code_point
+ return cp >= 0xDC00 and cp <= 0xDFFF
+ end
+
# Length of `self` in a UTF-8 String
- private fun u8char_len: Int do
+ fun u8char_len: Int do
var c = self.code_point
if c < 0x80 then return 1
if c <= 0x7FF then return 2
# assert [1, 2, 3].join(":") == "1:2:3"
# assert [1..3].join(":") == "1:2:3"
# assert [1..3].join == "123"
- fun join(separator: nullable Text): String
+ #
+ # if `last_separator` is given, then it is used to separate the last element.
+ #
+ # assert [1, 2, 3, 4].join(", ", " and ") == "1, 2, 3 and 4"
+ fun join(separator: nullable Text, last_separator: nullable Text): String
do
if is_empty then return ""
var e = i.item
if e != null then s.append(e.to_s)
+ if last_separator == null then last_separator = separator
+
# Concat other items
i.next
while i.is_ok do
- if separator != null then s.append(separator)
e = i.item
- if e != null then s.append(e.to_s)
i.next
+ if i.is_ok then
+ if separator != null then s.append(separator)
+ else
+ if last_separator != null then s.append(last_separator)
+ end
+ if e != null then s.append(e.to_s)
end
return s.to_s
end
end
redef class NativeString
- # Returns `self` as a new String.
+ # Get a `String` from the data at `self` copied into Nit memory
+ #
+ # Require: `self` is a null-terminated string.
fun to_s_with_copy: String is abstract
- # Returns `self` as a String of `length`.
+ # Get a `String` from `length` bytes at `self`
+ #
+ # The result may point to the data at `self` or
+ # it may make a copy in Nit controlled memory.
+ # This method should only be used when `self` was allocated by the Nit GC,
+ # or when manually controlling the deallocation of `self`.
fun to_s_with_length(length: Int): String is abstract
- # Returns a new instance of `String` with self as `_items`
+ # Get a `String` from the raw `length` bytes at `self`
#
- # /!\: Does not clean the items for compliance with UTF-8,
- # Use only if you know what you are doing
- fun to_s_unsafe(len: nullable Int): String is abstract
+ # The default value of `length` is the number of bytes before
+ # the first null character.
+ #
+ # The created `String` points to the data at `self`.
+ # This method should be used when `self` was allocated by the Nit GC,
+ # or when manually controlling the deallocation of `self`.
+ #
+ # /!\: This service does not clean the items for compliance with UTF-8,
+ # use only when the data has already been verified as valid UTF-8.
+ fun to_s_unsafe(length: nullable Int): String is abstract
- # Returns `self` as a String with `bytelen` and `length` set
+ # Get a `String` from the raw `bytelen` bytes at `self` with `unilen` Unicode characters
+ #
+ # The created `String` points to the data at `self`.
+ # This method should be used when `self` was allocated by the Nit GC,
+ # or when manually controlling the deallocation of `self`.
#
- # SEE: `abstract_text::Text` for more infos on the difference
- # between `Text::bytelen` and `Text::length`
+ # /!\: This service does not clean the items for compliance with UTF-8,
+ # use only when the data has already been verified as valid UTF-8.
+ #
+ # SEE: `abstract_text::Text` for more info on the difference
+ # between `Text::bytelen` and `Text::length`.
fun to_s_full(bytelen, unilen: Int): String is abstract
+
+ # Copies the content of `src` to `self`
+ #
+ # NOTE: `self` must be large enough to withold `self.bytelen` bytes
+ fun fill_from(src: Text) do src.copy_to_native(self, src.bytelen, 0, 0)
end
redef class NativeArray[E]