/*---------------------------------------------------------------------------
  Copyright 2012-2024, Microsoft Research, Daan Leijen.

  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the LICENSE file at the root of this distribution.
---------------------------------------------------------------------------*/

// Standard `:string` functions.
module std/core/stringstd/core/string

import std/core/typesstd/core/types
import std/core/hndstd/core/hnd
import std/core/intstd/core/int
import std/core/orderstd/core/order
import std/core/vectorstd/core/vector

extern import
  c  file "inline/string"
  js file "inline/string.js"


// Convert a character to a string
pub extern char/stringstd/core/string/char/string: (c : char) -> string : ( c : charstd/core/types/char: V ) -> stringstd/core/types/string: V
  c  "kk_string_from_char"
  cs inline "Primitive.CharToString(#1)"
  js inline "_char_to_string(#1)"

// Convert a vector of characters to a string.
pub extern vector/stringstd/core/string/vector/string: (vector<char>) -> string : (vectorstd/core/types/vector: V -> V<charstd/core/types/char: V>) -> stringstd/core/types/string: V
  c  "kk_string_from_chars"
  cs inline "Primitive.CharsToString(#1)"
  js inline "_chars_to_string(#1)"

// Convert a string to a vector of characters.
pub extern vectorstd/core/string/vector: (s : string) -> vector<char> : ( s : stringstd/core/types/string: V ) -> vectorstd/core/types/vector: V -> V<charstd/core/types/char: V>
  c  "kk_string_to_chars"
  cs inline "Primitive.StringToChars(#1)"
  js inline "_string_to_chars(#1)"

// Are two strings equal?
// Uses exact equality between character codes.
pub inline extern (==)std/core/string/(==): (string, string) -> bool : (stringstd/core/types/string: V,stringstd/core/types/string: V) -> boolstd/core/types/bool: V
  c  "kk_string_is_eq"
  cs inline "(#1 == #2)"
  js inline "(#1 === #2)"

// Are two strings not equal?
pub inline extern (!=)std/core/string/(!=): (string, string) -> bool : (stringstd/core/types/string: V,stringstd/core/types/string: V) -> boolstd/core/types/bool: V
  c  "kk_string_is_neq"
  inline "(#1 != #2)"
  js inline "(#1 !== #2)"

fip extern string-cmpstd/core/string/string-cmp: (x : string, y : string) -> int( ^xx: string : stringstd/core/types/string: V, ^yy: string : stringstd/core/types/string: V ) : intstd/core/types/int: V
  c  "kk_string_cmp_int_borrow"
  cs inline "String.Compare(#1,#2)"
  js inline "(#1===#2 ? 0 : (#1 > #2 ? 1 : -1))"

// Compare two strings.
// Uses the character codes directly for comparison
pub fip fun cmpstd/core/string/cmp: (x : string, y : string) -> order( ^xx: string : stringstd/core/types/string: V, ^yy: string : stringstd/core/types/string: V)result: -> total order : orderstd/core/types/order: V
  string-cmpstd/core/string/string-cmp: (x : string, y : string) -> int(xx: string,yy: string).orderstd/core/int/order: (i : int) -> order

pub fip fun order2std/core/string/order2: (x : string, y : string) -> (string, string)( xx: string : stringstd/core/types/string: V, yy: string : stringstd/core/types/string: V )result: -> total (string, string) : (std/core/types/tuple2: (V, V) -> Vstringstd/core/types/string: V,stringstd/core/types/string: V)
  if (xx: string<=std/core/string/(<=): (x : string, y : string) -> boolyy: string) then (std/core/types/Tuple2: forall<a,b> (fst : a, snd : b) -> (a, b)xx: string,yy: string)std/core/types/Tuple2: forall<a,b> (fst : a, snd : b) -> (a, b) else (std/core/types/Tuple2: forall<a,b> (fst : a, snd : b) -> (a, b)yy: string,xx: string)std/core/types/Tuple2: forall<a,b> (fst : a, snd : b) -> (a, b)

pub fip fun (>=)std/core/string/(>=): (x : string, y : string) -> bool( ^xx: string : stringstd/core/types/string: V, ^yy: string : stringstd/core/types/string: V )result: -> total bool : boolstd/core/types/bool: V { cmpstd/core/string/cmp: (x : string, y : string) -> order(xx: string,yy: string) >std/core/order/(>): (x : order, y : order) -> bool Ltstd/core/types/Lt: order }
pub fip fun (<=)std/core/string/(<=): (x : string, y : string) -> bool( ^xx: string : stringstd/core/types/string: V, ^yy: string : stringstd/core/types/string: V )result: -> total bool : boolstd/core/types/bool: V { cmpstd/core/string/cmp: (x : string, y : string) -> order(xx: string,yy: string) <std/core/order/(<): (x : order, y : order) -> bool Gtstd/core/types/Gt: order }
pub fip fun (>)std/core/string/(>): (x : string, y : string) -> bool( ^xx: string : stringstd/core/types/string: V, ^yy: string : stringstd/core/types/string: V )result: -> total bool : boolstd/core/types/bool: V  { cmpstd/core/string/cmp: (x : string, y : string) -> order(xx: string,yy: string) ==std/core/order/(==): (x : order, y : order) -> bool Gtstd/core/types/Gt: order }
pub fip fun (<)std/core/string/(<): (x : string, y : string) -> bool( ^xx: string : stringstd/core/types/string: V, ^yy: string : stringstd/core/types/string: V )result: -> total bool : boolstd/core/types/bool: V  { cmpstd/core/string/cmp: (x : string, y : string) -> order(xx: string,yy: string) ==std/core/order/(==): (x : order, y : order) -> bool Ltstd/core/types/Lt: order }

// Choose a non-empty string
pub fun (||)std/core/string/(||): (x : string, y : string) -> string( xx: string : stringstd/core/types/string: V, yy: string : stringstd/core/types/string: V )result: -> total string : stringstd/core/types/string: V
  if xx: string.is-emptystd/core/string/is-empty: (s : string) -> bool then yy: string else xx: string

// O(n). Return the number of characters in a string.
pub extern countstd/core/string/count: (s : string) -> int( ss: string : stringstd/core/types/string: V ) : intstd/core/types/int: V
  c  "kk_string_count_int"
  cs "Primitive.StringCount"
  js "_string_count"


// Does string `s` contain the string `sub`?
pub inline extern containsstd/core/string/contains: (s : string, sub : string) -> bool: (s : stringstd/core/types/string: V, sub : stringstd/core/types/string: V ) -> boolstd/core/types/bool: V
  c  "kk_string_contains"
  cs inline "((#1).Contains(#2))"
  js inline "((#1).indexOf(#2) >= 0)"

// Concatenate a vector of strings
pub inline extern vector/joinstd/core/string/vector/join: (v : vector<string>) -> string: (v : vectorstd/core/types/vector: V -> V<stringstd/core/types/string: V> ) -> stringstd/core/types/string: V
  c  "kk_string_join"
  cs "String.Concat"
  js inline "((#1).join(''))"

// Concatenate a vector of strings with a separator `sep`
pub inline extern vectorsep/joinstd/core/string/vectorsep/join: (v : vector<string>, sep : string) -> string: (v : vectorstd/core/types/vector: V -> V<stringstd/core/types/string: V>, sep : stringstd/core/types/string: V ) -> totalstd/core/types/total: E stringstd/core/types/string: V
  c  "kk_string_join_with"
  cs "Primitive.Concat"
  js inline "((#1).join(#2))"


extern repeatzstd/core/string/repeatz: (s : string, n : ssize_t) -> string( ss: string : stringstd/core/types/string: V, nn: ssize_t : ssize_tstd/core/types/ssize_t: V ) : stringstd/core/types/string: V
  c  "kk_string_repeat"
  cs "Primitive.Repeat"
  js "_string_repeat"

// Repeat a string `n` times
pub fun repeatstd/core/string/repeat: (s : string, n : int) -> string( ss: string : stringstd/core/types/string: V, ^nn: int : intstd/core/types/int: V )result: -> total string : stringstd/core/types/string: V
  repeatzstd/core/string/repeatz: (s : string, n : ssize_t) -> string(ss: string,nn: int.ssize_tstd/core/int/ssize_t: (i : int) -> ssize_t)

// Convert a `:maybe` string to a string using the empty sting for `Nothing`
pub fun maybe/stringstd/core/string/maybe/string: (ms : maybe<string>) -> string( msms: maybe<string> : maybestd/core/types/maybe: V -> V<stringstd/core/types/string: V> )result: -> total string : stringstd/core/types/string: V
  match msms: maybe<string>
    Nothingstd/core/types/Nothing: forall<a> maybe<a> -> ""literal: string
count= 0
Juststd/core/types/Just: forall<a> (value : a) -> maybe<a>(ss: string) ->
ss: string // Is a string empty? pub fun is-emptystd/core/string/is-empty: (s : string) -> bool( ss: string : stringstd/core/types/string: V )result: -> total bool : boolstd/core/types/bool: V ss: string ==std/core/string/(==): (string, string) -> bool ""literal: string
count= 0
// Is a string not empty? pub fun is-notemptystd/core/string/is-notempty: (s : string) -> bool( ss: string : stringstd/core/types/string: V )result: -> total bool : boolstd/core/types/bool: V ss: string !=std/core/string/(!=): (string, string) -> bool ""literal: string
count= 0
// Transform a string to a maybe type, using `Nothing` for an empty string pub fun maybestd/core/string/maybe: (s : string) -> maybe<string>( ss: string : stringstd/core/types/string: V )result: -> total maybe<string> : maybestd/core/types/maybe: V -> V<stringstd/core/types/string: V> if ss: string.is-emptystd/core/string/is-empty: (s : string) -> bool then Nothingstd/core/types/Nothing: forall<a> maybe<a> else Juststd/core/types/Just: forall<a> (value : a) -> maybe<a>(ss: string) // Replace every occurrence of `pattern` to `repl` in a string. pub inline extern replace-allstd/core/string/replace-all: (s : string, pattern : string, repl : string) -> string( s : stringstd/core/types/string: V, pattern : stringstd/core/types/string: V, repl : stringstd/core/types/string: V ) : stringstd/core/types/string: V c "kk_string_replace_all" cs inline "(#1).Replace(#2,#3)" js inline "(#1).replace(new RegExp((#2).replace(/[\\\\\\$\\^*+\\-{}?().]/g,'\\\\$&'),'g'),#3)" // Count occurrences of `pattern` in a string. pub inline extern stringpat/countstd/core/string/stringpat/count: (s : string, pattern : string) -> int( s : stringstd/core/types/string: V, pattern : stringstd/core/types/string: V ) : intstd/core/types/int: V c "kk_string_count_pattern" cs inline "Primitive.Count(#1,#2)" js inline "((#2) ? ((#1).match(new RegExp((#2).replace(/[\\\\\\$\\^*+\\-{}?().]/g,'\\\\$&'),'g'))||[]).length : 0)" // Convert a string to upper-case pub extern to-upperstd/core/string/to-upper: (s : string) -> string : (s : stringstd/core/types/string: V) -> stringstd/core/types/string: V c "kk_string_to_upper" cs inline "(#1).ToUpper()" js inline "(#1).toUpperCase()" // Convert a string to lower-case pub extern to-lowerstd/core/string/to-lower: (s : string) -> string : (s : stringstd/core/types/string: V) -> stringstd/core/types/string: V c "kk_string_to_lower" cs inline "(#1).ToLower()" js inline "(#1).toLowerCase()" // Right-align a string to width `width` using `fill` (default is a space) to fill from the left. pub fun pad-leftstd/core/string/pad-left: (s : string, width : int, fill : ? char) -> string( ss: string : stringstd/core/types/string: V, ^widthwidth: int : intstd/core/types/int: V, fillfill: ? char : charstd/core/types/char: V = ' 'literal: char
unicode= u0020
)result: -> total string : stringstd/core/types/string: V val ww: int = widthwidth: int val nn: int = ss: string.countstd/core/string/count: (s : string) -> int if ww: int <=std/core/int/(<=): (x : int, y : int) -> bool nn: int then ss: string else fillfill: char.stringstd/core/string/char/string: (c : char) -> string.repeatstd/core/string/repeat: (s : string, n : int) -> string( ww: int -std/core/int/(-): (x : int, y : int) -> int nn: int ) ++std/core/types/(++): (x : string, y : string) -> string
ss: string // Left-align a string to width `width` using `fill` (default is a space) to fill on the right. pub fun pad-rightstd/core/string/pad-right: (s : string, width : int, fill : ? char) -> string( ss: string : stringstd/core/types/string: V, ^widthwidth: int : intstd/core/types/int: V, fillfill: ? char : charstd/core/types/char: V = ' 'literal: char
unicode= u0020
)result: -> total string : stringstd/core/types/string: V val ww: int = widthwidth: int val nn: int = ss: string.countstd/core/string/count: (s : string) -> int if ww: int <=std/core/int/(<=): (x : int, y : int) -> bool nn: int then ss: string else ss: string ++std/core/types/(++): (x : string, y : string) -> string fillfill: char.stringstd/core/string/char/string: (c : char) -> string.repeatstd/core/string/repeat: (s : string, n : int) -> string(ww: int -std/core/int/(-): (x : int, y : int) -> int nn: int
) // Trim whitespace on the left and right side of a string pub fun trimstd/core/string/trim: (s : string) -> string( ss: string : stringstd/core/types/string: V )result: -> total string : stringstd/core/types/string: V ss: string.trim-leftstd/core/string/trim-left: (s : string) -> string.trim-rightstd/core/string/trim-right: (s : string) -> string // Trim the starting white space of a string pub inline extern trim-leftstd/core/string/trim-left: (s : string) -> string( s : stringstd/core/types/string: V ) : stringstd/core/types/string: V c "kk_string_trim_left" cs inline "(#1).TrimStart()" js inline "((#1).replace(/^\\s\\s*/,''))" // Trim the ending white space of a string. pub inline extern trim-rightstd/core/string/trim-right: (s : string) -> string( s : stringstd/core/types/string: V ) : stringstd/core/types/string: V c "kk_string_trim_right" cs inline "(#1).TrimEnd()" js inline "((#1).replace(/\\s+$/,''))" inline extern splitvstd/core/string/splitv: (s : string, sep : string) -> vector<string>( s : stringstd/core/types/string: V, sep : stringstd/core/types/string: V ) : vectorstd/core/types/vector: V -> V<stringstd/core/types/string: V> c "kk_string_splitv" cs inline "(#1.Split(new String[]{#2}, StringSplitOptions.None))" js inline "((#1).split(#2))" inline extern splitvnstd/core/string/splitvn: (s : string, sep : string, n : ssize_t) -> vector<string>( s : stringstd/core/types/string: V, sep : stringstd/core/types/string: V, n : ssize_tstd/core/types/ssize_t: V ) : vectorstd/core/types/vector: V -> V<stringstd/core/types/string: V> c "kk_string_splitv_atmost" cs inline "#1.Split(new String[]{#2},#3, StringSplitOptions.None)" js inline "(#1).split(#2, #3)" // Split a string into parts that were delimited by `sep`. The delimeters are not included in the results. // For example: `split("1,,2",",") == ["1","","2"]` pub fun splitstd/core/string/split: (s : string, sep : string) -> list<string>( ss: string : stringstd/core/types/string: V, sepsep: string : stringstd/core/types/string: V )result: -> total list<string> : liststd/core/types/list: V -> V<stringstd/core/types/string: V> splitvstd/core/string/splitv: (s : string, sep : string) -> vector<string>(ss: string,sepsep: string).liststd/core/vector/list: (v : vector<string>) -> list<string> // Split a string into at most `n` parts that were delimited by a string `sep`. The delimeters are not included in the results (except for possibly the final part). // For example: `split("1,2,3",",",2) == ["1","2,3"]` pub fun splitn/splitstd/core/string/splitn/split: (s : string, sep : string, n : int) -> list<string>( ss: string : stringstd/core/types/string: V, sepsep: string: stringstd/core/types/string: V, ^nn: int : intstd/core/types/int: V)result: -> total list<string> : liststd/core/types/list: V -> V<stringstd/core/types/string: V> splitvnstd/core/string/splitvn: (s : string, sep : string, n : ssize_t) -> vector<string>(ss: string,sepsep: string,nn: int.ssize_tstd/core/int/ssize_t: (i : int) -> ssize_t).liststd/core/vector/list: (v : vector<string>) -> list<string> // Convert a string to a list of characters pub extern liststd/core/string/list: (s : string) -> list<char>( ss: string : stringstd/core/types/string: V ) : totalstd/core/types/total: E liststd/core/types/list: V -> V<charstd/core/types/char: V> c "kk_string_to_list" cs inline "Primitive.StringToList(#1)" js inline "_string_to_list(#1)" // Convert a list of characters to a string pub extern listchar/stringstd/core/string/listchar/string: (cs : list<char>) -> string( cscs: list<char> : liststd/core/types/list: V -> V<charstd/core/types/char: V> ) : totalstd/core/types/total: E stringstd/core/types/string: V c "kk_string_from_list" cs inline "Primitive.ListToString(#1)" js inline "_list_to_string(#1)"