Package: src/packages/strings.fdoc
Strings¶
key | file |
---|---|
__init__.flx | share/lib/std/strings/__init__.flx |
string.flx | share/lib/std/strings/string.flx |
String handling¶
//[__init__.flx]
include "std/strings/string";
include "std/strings/cstring";
include "std/strings/ustr";
Strings¶
We have three string like things. cstring
is just
an alias for a NTBS (Null Terminated Byte String).
The workhorse string
type based on C++ string.
A ustring
is a unicode representation using a 32 bit unsigned integer as
the character base.
This type is deprecated, to be repalced by C++11 unicode string type.
//[string.flx]
typedef cstring = +char;
type string = "::std::basic_string<char>"
requires Cxx_headers::string,
header '#include "flx_serialisers.hpp"',
encoder "::flx::gc::generic::string_encoder",
decoder "::flx::gc::generic::string_decoder"
;
typedef strings = typesetof (string);
class Str [T] {
virtual fun str: T -> string;
}
class Repr [T with Str[T]] {
virtual fun repr (t:T) : string => str t;
}
class Show [T] {
inherit Str[T];
inherit Repr[T];
}
Equality and total ordering¶
//[string.flx]
instance[t in strings] Eq[t] {
fun == : t * t -> bool = "$1==$2";
}
instance[t in strings] Tord[t] {
fun < : t * t -> bool = "$1<$2";
}
class String
{
inherit Eq[string];
inherit Tord[string];
Equality of string
and char
¶
//[string.flx]
fun == (s:string, c:char) => len s == 1uz and s.[0] == c;
fun == (c:char, s:string) => len s == 1uz and s.[0] == c;
fun != (s:string, c:char) => len s != 1uz or s.[0] != c;
fun != (c:char, s:string) => len s != 1uz or s.[0] != c;
Append to string
object¶
//[string.flx]
proc += : &string * string = "$1->append($2:assign);";
proc += : &string * +char = "$1->append($2:assign);";
proc += : &string * char = "*$1 += $2;";
proc += : &string * &string = "$1->append(*$2);";
Length of string
¶
//[string.flx]
// we need to cast to an int so that c++ won't complain
fun len: string -> size = "$1.size()";
fun len: &string -> size = "$1->size()";
String concatenation.¶
//[string.flx]
fun + : string * string -> string = "$1+$2";
fun + : string * carray[char] -> string = "$1+$2";
fun + : string * char -> string = "$1+$2";
fun + : char * string -> string = "$1+$2";
//fun + : string * int -> string = "$1+::flx::rtl::i18n::utf8($2:assign)" is add requires package "flx_i18n";
fun + ( x:string, y: int) => x + str y;
// may be a bit risky!
// IT WAS: interferes with "hello" + list ("world","blah"):
// is this a string or a list of strings?
//fun + [T with Str[T]] (x:string, y:T) => x + str y;
Repetition of string
or char
¶
//[string.flx]
fun * : string * int -> string = "::flx::rtl::strutil::mul($1:assign,$2:assign)" requires package "flx_strutil";
fun * : char * int -> string = "::std::string($2:assign,$1:assign)";
Application of string
to string
or int
is concatenation¶
//[string.flx]
fun apply (x:string, y:string):string => x + y;
fun apply (x:string, y:int):string => x + y;
Construct a char from first byte of a string
.¶
Returns nul char (code 0) if the string is empty.
//[string.flx]
ctor char (x:string) => x.[0];
Constructors for string
¶
//[string.flx]
ctor string (c:char) => ""+c;
ctor string: +char = "::std::string($1:assign)";
ctor string: +char * !ints = "::std::string($1:assign,$2:assign)";
fun utf8: int -> string = "::flx::rtl::i18n::utf8($1)" requires package "flx_i18n";
Substrings¶
//[string.flx]
fun subscript: string * !ints -> char =
"::flx::rtl::strutil::subscript($1:assign,$2:assign)" requires package "flx_strutil";
fun copyfrom: string * !ints -> string =
"::flx::rtl::strutil::substr($1:assign,$2:assign,$1:postfix.size())" requires package "flx_strutil";
fun copyto: string * !ints -> string =
"::flx::rtl::strutil::substr($1:assign,0,$2:assign)" requires package "flx_strutil";
fun substring: string * !ints * !ints -> string =
"::flx::rtl::strutil::substr($1:assign,$2:assign,$3:assign)" requires package "flx_strutil";
fun subscript (x:string, s:slice[int]):string =>
match s with
| #Slice_all => substring (x, 0, x.len.int)
| Slice_from (start) => copyfrom (x, start)
| Slice_to_incl (end) => copyto (x, end + 1)
| Slice_to_excl (end) => copyto (x, end)
| Slice_range_incl (start, end) => substring (x, start, end + 1)
| Slice_range_excl (start, end) => substring (x, start, end)
| Slice_from_counted (start, count) => substring (x,start, start + count)
| Slice_one (index) => string x.[index]
endmatch
;
fun apply (s:slice[int], x:string) => subscript (x,s);
fun subscript (x:string, gs:gslice[int]):string = {
var r = "";
match gs with
| GSlice s => r = subscript(x,s);
| GSSList gsl =>
// this should be faster cause it cats a list of string which
// is linear in the number of strings
var sl = Empty[string];
for gs in gsl perform sl = subscript (x,gs) + sl;
r = sl.rev.(cat "");
| _ =>
for i in gs perform r += x.[i];
endmatch;
return r;
}
proc store: &string * !ints * char = "(*$1)[$2] = $3;";
Map a string char
by char
¶
//[string.flx]
fun map (f:char->char) (var x:string): string = {
if len x > 0uz do
for var i in 0uz upto (len x) - 1uz do
store(&x, i, f x.[i]);
done
done
return x;
}
STL string functions¶
These come in two flavours: the standard C++ operations
which return stl_npos
on failure, and a more Felix
like variant which uses an option
type.
//[string.flx]
const stl_npos: size = "::std::string::npos";
fun stl_find: string * string -> size = "$1.find($2)" is cast;
fun stl_find: string * string * size -> size = "$1.find($2,$3)" is cast;
fun stl_find: string * +char -> size = "$1.find($2)" is cast;
fun stl_find: string * +char * size -> size = "$1.find($2,$3)" is cast;
fun stl_find: string * char -> size = "$1.find($2)" is cast;
fun stl_find: string * char * size -> size = "$1.find($2,$3)" is cast;
fun find (s:string, e:string) : opt[size] => match stl_find (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find (s:string, e:string, i:size) : opt[size] => match stl_find (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find (s:string, e:+char) : opt[size] => match stl_find (s, e) with | i when i== stl_npos => None[size] | i => Some i endmatch;
fun find (s:string, e:+char, i:size) : opt[size] => match stl_find (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find (s:string, e:char) : opt[size] => match stl_find (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find (s:string, e:char, i:size) : opt[size] => match stl_find (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun stl_rfind: string * string -> size = "$1.rfind($2)";
fun stl_rfind: string * string * size -> size = "$1.rfind($2,$3)";
fun stl_rfind: string * +char-> size = "$1.rfind($2)";
fun stl_rfind: string * +char * size -> size = "$1.rfind($2,$3)";
fun stl_rfind: string * char -> size = "$1.rfind($2)";
fun stl_rfind: string * char * size -> size = "$1.rfind($2,$3)";
fun rfind (s:string, e:string) : opt[size] => match stl_rfind (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun rfind (s:string, e:string, i:size) : opt[size] => match stl_rfind (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun rfind (s:string, e:+char) : opt[size] => match stl_rfind (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun rfind (s:string, e:+char, i:size) : opt[size] => match stl_rfind (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun rfind (s:string, e:char) : opt[size] => match stl_rfind (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun rfind (s:string, e:char, i:size) : opt[size] => match stl_rfind (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun stl_find_first_of: string * string -> size = "$1.find_first_of($2)";
fun stl_find_first_of: string * string * size -> size = "$1.find_first_of($2,$3)";
fun stl_find_first_of: string * +char -> size = "$1.find_first_of($2)";
fun stl_find_first_of: string * +char * size -> size = "$1.find_first_of($2,$3)";
fun stl_find_first_of: string * char -> size = "$1.find_first_of($2)";
fun stl_find_first_of: string * char * size -> size = "$1.find_first_of($2,$3)";
fun find_first_of (s:string, e:string) : opt[size] => match stl_find_first_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_of (s:string, e:string, i:size) : opt[size] => match stl_find_first_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_of (s:string, e:+char) : opt[size] => match stl_find_first_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_of (s:string, e:+char, i:size) : opt[size] => match stl_find_first_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_of (s:string, e:char) : opt[size] => match stl_find_first_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_of (s:string, e:char, i:size) : opt[size] => match stl_find_first_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun stl_find_first_not_of: string * string -> size = "$1.find_first_not_of($2)";
fun stl_find_first_not_of: string * string * size -> size = "$1.find_first_not_of($2,$3)";
fun stl_find_first_not_of: string * +char -> size = "$1.find_first_not_of($2)";
fun stl_find_first_not_of: string * +char * size -> size = "$1.find_first_not_of($2,$3)";
fun stl_find_first_not_of: string * char -> size = "$1.find_first_not_of($2)";
fun stl_find_first_not_of: string * char * size -> size = "$1.find_first_not_of($2,$3)";
fun find_first_not_of (s:string, e:string) : opt[size] => match stl_find_first_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_not_of (s:string, e:string, i:size) : opt[size] => match stl_find_first_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_not_of (s:string, e:+char) : opt[size] => match stl_find_first_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_not_of (s:string, e:+char, i:size) : opt[size] => match stl_find_first_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_not_of (s:string, e:char) : opt[size] => match stl_find_first_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_first_not_of (s:string, e:char, i:size) : opt[size] => match stl_find_first_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun stl_find_last_of: string * string -> size = "$1.find_last_of($2)";
fun stl_find_last_of: string * string * size -> size = "$1.find_last_of($2,$3)";
fun stl_find_last_of: string * +char -> size = "$1.find_last_of($2)";
fun stl_find_last_of: string * +char * size -> size = "$1.find_last_of($2,$3)";
fun stl_find_last_of: string * char -> size = "$1.find_last_of($2)";
fun stl_find_last_of: string * char * size -> size = "$1.find_last_of($2,$3)";
fun find_last_of (s:string, e:string) : opt[size] => match stl_find_last_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_of (s:string, e:string, i:size) : opt[size] => match stl_find_last_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_of (s:string, e:+char) : opt[size] => match stl_find_last_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_of (s:string, e:+char, i:size) : opt[size] => match stl_find_last_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_of (s:string, e:char) : opt[size] => match stl_find_last_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_of (s:string, e:char, i:size) : opt[size] => match stl_find_last_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun stl_find_last_not_of: string * string -> size = "$1.find_last_not_of($2)";
fun stl_find_last_not_of: string * string * size -> size = "$1.find_last_not_of($2,$3)";
fun stl_find_last_not_of: string * +char -> size = "$1.find_last_not_of($2)";
fun stl_find_last_not_of: string * +char * size -> size = "$1.find_last_not_of($2,$3)";
fun stl_find_last_not_of: string * char -> size = "$1.find_last_not_of($2)";
fun stl_find_last_not_of: string * char * size -> size = "$1.find_last_not_of($2,$3)";
fun find_last_not_of (s:string, e:string) : opt[size] => match stl_find_last_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_not_of (s:string, e:string, i:size) : opt[size] => match stl_find_last_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_not_of (s:string, e:+char) : opt[size] => match stl_find_last_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_not_of (s:string, e:+char, i:size) : opt[size] => match stl_find_last_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_not_of (s:string, e:char) : opt[size] => match stl_find_last_not_of (s, e) with | i when i == stl_npos => None[size] | i => Some i endmatch;
fun find_last_not_of (s:string, e:char, i:size) : opt[size] => match stl_find_last_not_of (s, e, i) with | i when i == stl_npos => None[size] | i => Some i endmatch;
Construe string
as set of char
¶
//[string.flx]
instance Set[string,char] {
fun \in (c:char, s:string) => stl_find (s,c) != stl_npos;
}
Construe string
as stream of char
¶
//[string.flx]
instance Iterable[string, char] {
gen iterator(var x:string) () = {
for var i in 0 upto x.len.int - 1 do yield Some (x.[i]); done
return None[char];
}
}
inherit Streamable[string,char];
Test if a string has given prefix or suffix¶
//[string.flx]
fun prefix(arg:string,key:string)=>
arg.[to len key]==key
;
fun suffix (arg:string,key:string)=>
arg.[-key.len to]==key
;
fun startswith (x:string) (e:string) : bool => prefix (x,e);
// as above: slices are faster
fun endswith (x:string) (e:string) : bool => suffix (x,e);
fun startswith (x:string) (e:char) : bool => x.[0] == e;
fun endswith (x:string) (e:char) : bool => x.[-1] == e;
Trim off specified prefix or suffix or both¶
//[string.flx]
fun ltrim (x:string) (e:string) : string =>
if startswith x e then
x.[e.len.int to]
else
x
endif
;
fun rtrim (x:string) (e:string) : string =>
if endswith x e then
x.[to x.len.int - e.len.int]
else
x
endif
;
fun trim (x:string) (e:string) : string => ltrim (rtrim x e) e;
Strip characters from left, right, or both end of a string.¶
//[string.flx]
fun lstrip (x:string, e:string) : string =
{
if len x > 0uz do
for var i in 0uz upto len x - 1uz do
var found = false;
for var j in 0uz upto len e - 1uz do
if x.[i] == e.[j] do
found = true;
done
done
if not found do
return x.[i to];
done
done;
done
return '';
}
fun rstrip (x:string, e:string) : string =
{
if len x > 0uz do
for var i in len x - 1uz downto 0uz do
var found = false;
for var j in 0uz upto len e - 1uz do
if x.[i] == e.[j] do
found = true;
done
done
if not found do
return x.[to i.int + 1];
done
done
done
return '';
}
fun strip (x:string, e:string) : string => lstrip(rstrip(x, e), e);
fun lstrip (x:string) : string => lstrip(x, " \t\n\r\f\v");
fun rstrip (x:string) : string => rstrip(x, " \t\n\r\f\v");
fun strip (x:string) : string => lstrip$ rstrip x;
Justify string contents¶
//[string.flx]
fun ljust(x:string, width:int) : string =>
if x.len.int >= width
then x
else x + (' ' * (width - x.len.int))
endif
;
fun rjust(x:string, width:int) : string =>
if x.len.int >= width
then x
else (' ' * (width - x.len.int)) + x
endif
;
Split a string into a list on given separator¶
//[string.flx]
fun split (x:string, d:char): List::list[string] => unbox (List::rev (rev_split (x,d)));
fun rev_split (x:string, d:char): uniq (List::list[string]) = {
fun aux (x:string,y:List::list[string]) =>
match find (x, d) with
| #None => Cons (x, y)
| Some n => aux$ x.[n+1uz to], List::Cons (x.[to n],y)
endmatch
;
return box (aux$ x, List::Empty[string]);
}
fun split (x:string, d:string): List::list[string] => unbox (List::rev (rev_split (x,d)));
fun rev_split (x:string, d:string): uniq List::list[string] = {
fun aux (pos:size,y:List::list[string]) =>
match stl_find_first_of (x, d, pos) with
| $(stl_npos) => List::Cons (x.[pos to],y)
| n => aux$ (n+1uz), List::Cons (x.[pos to n],y)
endmatch
;
return box (aux$ 0uz, List::Empty[string]);
}
fun split (x:string, d:+char): List::list[string] => unbox (List::rev (rev_split (x,d)));
fun rev_split (x:string, d:+char): uniq (List::list[string]) = {
fun aux (x:string,y:List::list[string]) =>
match find_first_of (x, d) with
| #None => List::Cons (x, y)
| Some n => aux$ x.[n+1uz to], List::Cons (x.[to n],y)
endmatch
;
return box (aux$ x, List::Empty[string]);
}
fun split_first (x:string, d:string): opt[string*string] =>
match find_first_of (x, d) with
| #None => None[string*string]
| Some n => Some (x.[to n],substring(x,n+1uz,(len x)))
endmatch
;
//$ Split a string on whitespace but respecting
//$ double quotes, single quotes, and slosh escapes.
// leading and trailing space is removed. Embedded
// multiple spaces cause a single split.
class RespectfulParser {
variant quote_action_t =
| ignore-quote
| keep-quote
| drop-quote
;
variant dquote_action_t =
| ignore-dquote
| keep-dquote
| drop-dquote
;
variant escape_action_t =
| ignore-escape
| keep-escape
| drop-escape
;
typedef action_t = (quote:quote_action_t, dquote:dquote_action_t, escape:escape_action_t);
variant mode_t = | copying | skipping | quote | dquote | escape-copying | escape-quote | escape-dquote;
typedef state_t = (mode:mode_t, current:string, parsed: list[string] );
noinline fun respectful_parse (action:action_t) (var state:state_t) (var s:string) : state_t =
{
var mode = state.mode;
var current = state.current;
var result = Empty[string];
noinline proc handlecopying(ch:char) {
if ch == char "'" do
match action.quote with
| #ignore-quote =>
current += ch;
| #keep-quote =>
current += ch;
mode = quote;
| #drop-quote =>
mode = quote;
endmatch;
elif ch == char '"' do
match action.dquote with
| #ignore-dquote =>
current += ch;
| #keep-dquote =>
current += ch;
mode = dquote;
| #drop-dquote =>
mode = dquote;
endmatch;
elif ch == char '\\' do
match action.escape with
| #ignore-escape =>
current += ch;
| #keep-escape =>
current += ch;
mode = escape-copying;
| #drop-escape =>
mode = escape-copying;
endmatch;
elif ord ch <= ' '.char.ord do // can't happen if called from skipping
result += current;
current = "";
mode = skipping;
else
current += ch;
mode = copying;
done
}
for ch in s do
match mode with
| #copying => handlecopying ch;
| #quote =>
if ch == char "'" do
match action.quote with
| #ignore-quote =>
assert false;
//current += ch;
| #keep-quote =>
current += ch;
mode = copying;
| #drop-quote =>
mode = copying;
endmatch;
elif ch == char "\\" do
match action.escape with
| #ignore-escape =>
current += ch;
| #keep-escape =>
current += ch;
mode = escape-quote;
| #drop-escape =>
mode = escape-quote;
endmatch;
else
current += ch;
done
| #dquote =>
if ch == char '"' do
match action.dquote with
| #ignore-dquote =>
assert false;
//current += ch;
| #keep-dquote =>
current += ch;
mode = copying;
| #drop-dquote =>
mode = copying;
endmatch;
elif ch == char "\\" do
match action.escape with
| #ignore-escape =>
current += ch;
| #keep-escape =>
current += ch;
mode = escape-dquote;
| #drop-escape =>
mode = escape-dquote;
endmatch;
else
current += ch;
done
| #escape-copying =>
current += ch;
mode = copying;
| #escape-quote =>
current += ch;
mode = quote;
| #escape-dquote =>
current += ch;
mode = dquote;
| #skipping =>
if ord ch > ' '.char.ord do
handlecopying ch;
done
endmatch;
done
return (mode=mode, current=current, parsed=state.parsed + result);
}
}
// simplified one shot parser.
// ignores mismatched quotes and backslashes.
fun respectful_split (action:RespectfulParser::action_t) (s:string) : list[string] =
{
var state = RespectfulParser::respectful_parse
action
(
mode=RespectfulParser::skipping,
current="",
parsed=Empty[string]
)
s
;
// ignore mismatched quotes and backslashes.
match state.mode with
| #skipping => ;
| _ => &state.parsed <- state.parsed + state.current;
endmatch;
return state.parsed;
}
fun respectful_split (s:string) : list[string] =>
respectful_split (
quote=RespectfulParser::keep-quote,
dquote=RespectfulParser::keep-dquote,
escape=RespectfulParser::keep-escape
)
s
;
// OO version of the parser.
object respectfulParser (action:RespectfulParser::action_t) = {
var state = (mode=RespectfulParser::skipping, current="", parsed=Empty[string]);
method proc parse (s:string) {
state = RespectfulParser::respectful_parse action state s;
}
method fun get_parsed () => state.parsed;
}
erase, insert or replace substrings¶
//[string.flx]
// Note: pos, length!
//$ mutators
proc erase: &string * size * size = "$1->erase($2,$3);";
proc insert: &string * size * string = "$1->insert($2,$3);";
proc replace: &string * size * size * string = "$1->replace($2,$3,$4);";
//$ functional
fun erase: string * size * size -> string = "::std::string($1).erase($2,$3)";
fun insert: string * size * string -> string = "::std::string($1).insert($2,$3)";
fun replace: string * size * size * string -> string = "::std::string($1).replace($2,$3,$4)";
search and replace¶
Search and replace by string.
//[string.flx]
fun search_and_replace (x:string, var spos:size, s:string, r:string) : string =
{
val m = s.len;
var o = x.[to spos];
var n = (x,s,spos).stl_find;
while n != stl_npos do
o+=x.[spos to n]+r;
spos = n+m;
n = (x,s,spos).stl_find.size;
done
o+=x.[spos to];
return o;
}
fun search_and_replace (x:string, s:string, r:string) : string => search_and_replace (x,0uz,s,r);
fun search_and_replace (vs:list[string * string]) (var v:string) = {
match k,b in vs do
v = search_and_replace (v,k,b);
done
return v;
}
Regexp search and replace¶
Uses Google RE2 engine.
//[string.flx]
// Replace \0 \1 \2 etc in s with text from v
fun subst(s:string, v:varray[StringPiece]): string =
{
//println$ "Subst " + s +" with " + str v;
enum mode_t {cp, ins};
var b = "";
var mode=cp;
var j = 0;
var count = 0;
for var i in 0 upto s.len.int - 1 do
match mode with
| #cp =>
if s.[i] == char "\\" do
mode = ins;
j=0; count = 0;
else
b += s.[i];
done
| #ins =>
if s.[i] in "0123456789" do
j = j * 10 + ord(s.[i]) - ord (char "0");
++count;
else
if count == 0 do
b += "\\";
elif j < v.len.int do
b+= str v.stl_begin.j;
done
// adjacent insertion?
if s.[i] == char "\\" do
j=0; count=0;
else
mode = cp;
b += s.[i];
done
done
endmatch;
done
// run off end
match mode with
| #cp => ;
| #ins =>
if count == 0 do
b += "\\";
elif j < v.len.int do
b+= str v.j;
done
endmatch;
return b;
}
// Search for regex, replace by r with \0 \1 \2 etc replace by match groups.
fun search_and_replace (x:string, var spos: size, re:Re2::RE2, r:string) : string =
{
var ngroups = re.NumberOfCapturingGroups + 1;
var v = varray[StringPiece]$ (ngroups+1).size, StringPiece "";
var o = x.[to spos]; // initial substring
var sp = StringPiece(x);
var base : +char = sp.data; // base pointer of char array
while Re2::Match(re, sp, spos.int, UNANCHORED, v.stl_begin, v.len.int) do
var mpos = size(v.0.data - base); // start of match
o+= x.[spos to mpos]; // copy upto start of match
o+= subst(r,v); // copy replacement
spos = mpos + v.0.len; // advance over match
done
o+=x.[spos to]; // rest of string
return o;
}
Parse string to numeric type¶
//[string.flx]
fun atoi: string -> int = "::std::atoi($1:postfix.c_str())" requires Cxx_headers::cstdlib;
fun atol: string -> long = "::std::atol($1:postfix.c_str())" requires Cxx_headers::cstdlib;
fun atoll: string -> long = "::std::atoll($1:postfix.c_str())" requires Cxx_headers::cstdlib;
fun atof: string -> double = "::std::atof($1:postfix.c_str())" requires Cxx_headers::cstdlib;
Reserve store¶
//[string.flx]
proc reserve: &string * !ints = "$1->reserve($2);";
Fetch underlying cstring.¶
//[string.flx]
// safely returns a malloc()'d copy, not garbage collected
fun _unsafe_cstr: string -> +char = "::flx::rtl::strutil::flx_cstr($1)" is atom;
// partially unsafe because the string could be modified.
fun stl_begin: &string -> +char = "((char*)$1->c_str())" is atom;
fun stl_end: &string -> +char = "((char*)($1->c_str()+$1->size()))" is atom;
// this operation returns a char pointer to GC managed storage
fun cstr (var s:string) => s.varray[char].stl_begin;
Polymorphic vsprintf hack¶
//[string.flx]
fun vsprintf[t]: +char * t -> string =
"::flx::rtl::strutil::flx_asprintf($1,$2)" requires package "flx_strutil"
;
fun vsprintf[t]: string * t -> string =
"::flx::rtl::strutil::flx_asprintf(const_cast<char*>($1.c_str()),$2)" requires package "flx_strutil"
;
Case translation¶
//[string.flx]
// Convert all characters to upper case
fun toupper(s:string):string => map (toupper of char) s;
// Convert all characters to lower case
fun tolower(s:string):string => map (tolower of char) s;
}
Transation to string¶
//[string.flx]
instance Str[string] {
fun str (s:string) : string => s;
}
instance Str[+char] {
fun str: +char -> string = '::flx::rtl::strutil::atostr($1)' requires package "flx_strutil";
}
instance Repr[string] {
fun repr (x:string) : string = {
var o = "'";
if len x > 0uz do
for var i in 0uz upto (String::len x) - 1uz do
o += repr x.[i];
done
done
return o + "'";
}
}
open[T in strings] Show[T];
open Set[string,char];