#
# assert " \n\thello \n\t".l_trim == "hello \n\t"
#
- # A whitespace is defined as any character which ascii value is less than or equal to 32
+ # `Char::is_whitespace` determines what is a whitespace.
fun l_trim: SELFTYPE
do
var iter = self.chars.iterator
while iter.is_ok do
- if iter.item.ascii > 32 then break
+ if not iter.item.is_whitespace then break
iter.next
end
if iter.index == length then return self.empty
#
# assert " \n\thello \n\t".r_trim == " \n\thello"
#
- # A whitespace is defined as any character which ascii value is less than or equal to 32
+ # `Char::is_whitespace` determines what is a whitespace.
fun r_trim: SELFTYPE
do
var iter = self.chars.reverse_iterator
while iter.is_ok do
- if iter.item.ascii > 32 then break
+ if not iter.item.is_whitespace then break
iter.next
end
if iter.index < 0 then return self.empty
end
# Trims trailing and preceding white spaces
- # A whitespace is defined as any character which ascii value is less than or equal to 32
#
# assert " Hello World ! ".trim == "Hello World !"
# assert "\na\nb\tc\t".trim == "a\nb\tc"
+ #
+ # `Char::is_whitespace` determines what is a whitespace.
fun trim: SELFTYPE do return (self.l_trim).r_trim
+ # Is the string non-empty but only made of whitespaces?
+ #
+ # assert " \n\t ".is_whitespace == true
+ # assert " hello ".is_whitespace == false
+ # assert "".is_whitespace == false
+ #
+ # `Char::is_whitespace` determines what is a whitespace.
+ fun is_whitespace: Bool
+ do
+ if is_empty then return false
+ for c in self.chars do
+ if not c.is_whitespace then return false
+ end
+ return true
+ end
+
# Returns `self` removed from its last line terminator (if any).
#
# assert "Hello\n".chomp == "Hello"
# assert "\r\n\r\n".chomp == "\r\n"
# assert "\r\n\r".chomp == "\r\n"
#
- # Note: unlike with most IO methods like `IStream::read_line`,
+ # Note: unlike with most IO methods like `Reader::read_line`,
# a single `\r` is considered here to be a line terminator and will be removed.
fun chomp: SELFTYPE
do
# * 1.0 for right-justified (all spaces at the left)
# * 0.5 for centered (half the spaces at the left)
#
+ # 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(2, 0.0) == "hello"
#
- # REQUIRE: left >= 0.0 and left <= 1.0
+ # 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): SELFTYPE
+ # ENSURE: `self.length >= length implies result == self`
+ fun justify(length: Int, left: Float): String
do
var diff = length - self.length
- if diff <= 0 then return self
+ 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)
end
- # Mangle a string to be a unique string only made of alphanumeric characters
+ # Mangle a string to be a unique string only made of alphanumeric characters and underscores.
+ #
+ # This method is injective (two different inputs never produce the same
+ # output) and the returned string always respect the following rules:
+ #
+ # * Contains only US-ASCII letters, digits and underscores.
+ # * Never starts with a digit.
+ # * Never ends with an underscore.
+ # * Never contains two contiguous underscores.
+ #
+ # assert "42_is/The answer!".to_cmangle == "_52d2_is_47dThe_32danswer_33d"
+ # assert "__".to_cmangle == "_95d_95d"
+ # assert "__d".to_cmangle == "_95d_d"
+ # assert "_d_".to_cmangle == "_d_95d"
+ # assert "_42".to_cmangle == "_95d42"
+ # assert "foo".to_cmangle == "foo"
+ # assert "".to_cmangle == ""
fun to_cmangle: String
do
+ if is_empty then return ""
var res = new FlatBuffer
var underscore = false
- for i in [0..length[ do
- var c = chars[i]
+ var start = 0
+ var c = chars[0]
+
+ if c >= '0' and c <= '9' then
+ res.add('_')
+ res.append(c.ascii.to_s)
+ res.add('d')
+ start = 1
+ end
+ for i in [start..length[ do
+ c = chars[i]
if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
res.add(c)
underscore = false
underscore = false
end
end
+ if underscore then
+ res.append('_'.ascii.to_s)
+ res.add('d')
+ end
return res.to_s
end
# assert "a&b-<>\"x\"/'".html_escape == "a&b-<>"x"/'"
#
# SEE: <https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content>
- fun html_escape: SELFTYPE
+ fun html_escape: String
do
var buf = new FlatBuffer
abstract class String
super Text
- redef type SELFTYPE: String
+ redef type SELFTYPE: String is fixed
redef fun to_s do return self
#
# SEE : `Char::is_letter` for the definition of letter.
#
- # assert "jAVASCRIPT".capitalized == "Javascript"
- # assert "i am root".capitalized == "I Am Root"
- # assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
+ # assert "jAVASCRIPT".capitalized == "Javascript"
+ # assert "i am root".capitalized == "I Am Root"
+ # assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
fun capitalized: SELFTYPE do
if length == 0 then return self
# Indes in _items of the last item of the string
private var index_to: Int is noinit
- redef var chars: SequenceRead[Char] = new FlatStringCharView(self)
+ redef var chars: SequenceRead[Char] = new FlatStringCharView(self) is lazy
redef fun [](index)
do
from = 0
end
- var realFrom = index_from + from
+ var new_from = index_from + from
- if (realFrom + count) > index_to then return new FlatString.with_infos(items, index_to - realFrom + 1, realFrom, index_to)
+ if (new_from + count) > index_to then
+ var new_len = index_to - new_from + 1
+ if new_len <= 0 then return empty
+ return new FlatString.with_infos(items, new_len, new_from, index_to)
+ end
- if count == 0 then return empty
+ if count <= 0 then return empty
- var to = realFrom + count - 1
+ var to = new_from + count - 1
- return new FlatString.with_infos(items, to - realFrom + 1, realFrom, to)
+ return new FlatString.with_infos(items, to - new_from + 1, new_from, to)
end
redef fun empty do return "".as(FlatString)
abstract class Buffer
super Text
- redef type SELFTYPE: Buffer
+ redef type SELFTYPE: Buffer is fixed
# Specific implementations MUST set this to `true` in order to invalidate caches
protected var is_dirty = true
#
# SEE: `Char::is_letter` for the definition of a letter.
#
- # var b = new FlatBuffer.from("jAVAsCriPt")
- # b.capitalize
- # assert b == "Javascript"
- # b = new FlatBuffer.from("i am root")
- # b.capitalize
- # assert b == "I Am Root"
- # b = new FlatBuffer.from("ab_c -ab0c ab\nc")
- # b.capitalize
- # assert b == "Ab_C -Ab0C Ab\nC"
+ # var b = new FlatBuffer.from("jAVAsCriPt")
+ # b.capitalize
+ # assert b == "Javascript"
+ # b = new FlatBuffer.from("i am root")
+ # b.capitalize
+ # assert b == "I Am Root"
+ # b = new FlatBuffer.from("ab_c -ab0c ab\nc")
+ # b.capitalize
+ # assert b == "Ab_C -Ab0C Ab\nC"
fun capitalize do
if length == 0 then return
var c = self[0].to_upper
super FlatText
super Buffer
- redef type SELFTYPE: FlatBuffer
-
- redef var chars: Sequence[Char] = new FlatBufferCharView(self)
+ redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
private var capacity: Int = 0
# The class name of the object.
#
- # assert 5.class_name == "Int"
+ # assert 5.class_name == "Int"
fun class_name: String do return native_class_name.to_s
# Developer readable representation of `self`.
end
end
+ # C function to calculate the length of the `NativeString` to receive `self`
+ private fun int_to_s_len: Int is extern "native_int_length_str"
+
# C function to convert an nit Int to a NativeString (char*)
- private fun native_int_to_s: NativeString is extern "native_int_to_s"
+ private fun native_int_to_s(nstr: NativeString, strlen: Int) is extern "native_int_to_s"
# return displayable int in base 10 and signed
#
# assert 1.to_s == "1"
# assert (-123).to_s == "-123"
redef fun to_s do
- return native_int_to_s.to_s
+ var nslen = int_to_s_len
+ var ns = new NativeString(nslen + 1)
+ ns[nslen] = '\0'
+ native_int_to_s(ns, nslen + 1)
+ return ns.to_s_with_length(nslen)
end
# return displayable int in hexadecimal
return p1 + "." + p2
end
-
- # `self` representation with `nb` digits after the '.'.
- #
- # assert 12.345.to_precision_native(1) == "12.3"
- # assert 12.345.to_precision_native(2) == "12.35"
- # assert 12.345.to_precision_native(3) == "12.345"
- # assert 12.345.to_precision_native(4) == "12.3450"
- fun to_precision_native(nb: Int): String import NativeString.to_s `{
- int size;
- char *str;
-
- size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
- str = malloc(size + 1);
- sprintf(str, "%.*f", (int)nb, recv );
-
- return NativeString_to_s( str );
- `}
end
redef class Char
# Returns true if the char is a numerical digit
#
- # assert '0'.is_numeric
- # assert '9'.is_numeric
- # assert not 'a'.is_numeric
- # assert not '?'.is_numeric
+ # assert '0'.is_numeric
+ # assert '9'.is_numeric
+ # assert not 'a'.is_numeric
+ # assert not '?'.is_numeric
fun is_numeric: Bool
do
return self >= '0' and self <= '9'
# Returns true if the char is an alpha digit
#
- # assert 'a'.is_alpha
- # assert 'Z'.is_alpha
- # assert not '0'.is_alpha
- # assert not '?'.is_alpha
+ # assert 'a'.is_alpha
+ # assert 'Z'.is_alpha
+ # assert not '0'.is_alpha
+ # assert not '?'.is_alpha
fun is_alpha: Bool
do
return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
# Returns true if the char is an alpha or a numeric digit
#
- # assert 'a'.is_alphanumeric
- # assert 'Z'.is_alphanumeric
- # assert '0'.is_alphanumeric
- # assert '9'.is_alphanumeric
- # assert not '?'.is_alphanumeric
+ # assert 'a'.is_alphanumeric
+ # assert 'Z'.is_alphanumeric
+ # assert '0'.is_alphanumeric
+ # assert '9'.is_alphanumeric
+ # assert not '?'.is_alphanumeric
fun is_alphanumeric: Bool
do
return self.is_numeric or self.is_alpha
var i = iterator
var k = i.key
var e = i.item
- s.append("{k}{couple_sep}{e or else "<null>"}")
+ s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
# Concat other items
i.next
s.append(sep)
k = i.key
e = i.item
- s.append("{k}{couple_sep}{e or else "<null>"}")
+ s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
i.next
end
return s.to_s