diff options
author | Louis Pilfold <louis@lpil.uk> | 2021-07-21 20:14:08 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2021-07-21 20:14:08 +0100 |
commit | 19cd3b5118eb417515e48c4fd3cfa33ede15f860 (patch) | |
tree | f2df979533c165ff19984e4c83275508291567d7 /src | |
parent | 82309771c53d6cff95aed403496b5c13584148eb (diff) | |
download | gleam_stdlib-19cd3b5118eb417515e48c4fd3cfa33ede15f860.tar.gz gleam_stdlib-19cd3b5118eb417515e48c4fd3cfa33ede15f860.zip |
Convert more string functions
Diffstat (limited to 'src')
-rw-r--r-- | src/gleam/int.gleam | 2 | ||||
-rw-r--r-- | src/gleam/string.gleam | 146 | ||||
-rw-r--r-- | src/gleam/string_builder.gleam | 292 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 37 | ||||
-rw-r--r-- | src/gleam_stdlib.js | 44 |
5 files changed, 340 insertions, 181 deletions
diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam index 0ce53c4..f3c49c8 100644 --- a/src/gleam/int.gleam +++ b/src/gleam/int.gleam @@ -62,7 +62,7 @@ if erlang { if javascript { external fn do_to_string(Int) -> String = - "../gleam_stdlib.js" "int_to_string" + "../gleam_stdlib.js" "to_string" } /// Prints a given int to a string using the base number provided. diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index 4701afd..8379061 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -122,7 +122,7 @@ if erlang { if javascript { external fn do_lowercase(String) -> String = - "../gleam_stdlib.js" "string_lowercase" + "../gleam_stdlib.js" "lowercase" } /// Creates a new string with all the graphemes in the input string converted to @@ -146,25 +146,43 @@ if erlang { if javascript { external fn do_uppercase(String) -> String = - "../gleam_stdlib.js" "string_uppercase" + "../gleam_stdlib.js" "uppercase" +} + +/// Compares two strings to see which is "larger" by comparing their graphemes. +/// +/// This does not compare the size or length of the given strings. +/// +/// ## Examples +/// +/// > compare("Anthony", "Anthony") +/// order.Eq +/// +/// > compare("A", "B") +/// order.Lt +/// +pub fn compare(a: String, b: String) -> order.Order { + case a == b { + True -> order.Eq + _ -> + case less_than(a, b) { + True -> order.Lt + _ -> order.Gt + } + } } if erlang { - /// Compares two strings to see which is "larger" by comparing their graphemes. - /// - /// This does not compare the size or length of the given strings. - /// - /// ## Examples - /// - /// > compare("Anthony", "Anthony") - /// order.Eq - /// - /// > compare("A", "B") - /// order.Lt - /// - pub external fn compare(String, String) -> order.Order = - "gleam_stdlib" "compare_strings" + external fn less_than(String, String) -> Bool = + "gleam_stdlib" "less_than" +} +if javascript { + external fn less_than(String, String) -> Bool = + "../gleam_stdlib.js" "less_than" +} + +if erlang { external fn erl_slice(String, Int, Int) -> String = "string" "slice" @@ -290,21 +308,23 @@ if erlang { /// pub external fn ends_with(String, String) -> Bool = "gleam_stdlib" "string_ends_with" +} - /// Creates a list of strings by splitting a given string on a given substring. - /// - /// ## Examples - /// - /// > split("home/gleam/desktop/", on: "/") - /// ["home", "gleam", "desktop", ""] - /// - pub fn split(x: String, on substring: String) -> List(String) { - x - |> string_builder.from_string - |> string_builder.split(on: substring) - |> list.map(with: string_builder.to_string) - } +/// Creates a list of strings by splitting a given string on a given substring. +/// +/// ## Examples +/// +/// > split("home/gleam/desktop/", on: "/") +/// ["home", "gleam", "desktop", ""] +/// +pub fn split(x: String, on substring: String) -> List(String) { + x + |> string_builder.from_string + |> string_builder.split(on: substring) + |> list.map(with: string_builder.to_string) +} +if erlang { external fn erl_split(String, String) -> List(String) = "string" "split" @@ -329,42 +349,44 @@ if erlang { _ -> Error(Nil) } } +} - /// Creates a new string by joining two strings together. - /// - /// This function copies both strings and runs in linear time. If you find - /// yourself joining strings frequently consider using the [string_builder](../string_builder) - /// module as it can append strings much faster! - /// - /// ## Examples - /// - /// > append(to: "butter", suffix: "fly") - /// "butterfly" - /// - pub fn append(to first: String, suffix second: String) -> String { - first - |> string_builder.from_string - |> string_builder.append(second) - |> string_builder.to_string - } +/// Creates a new string by joining two strings together. +/// +/// This function copies both strings and runs in linear time. If you find +/// yourself joining strings frequently consider using the [string_builder](../string_builder) +/// module as it can append strings much faster! +/// +/// ## Examples +/// +/// > append(to: "butter", suffix: "fly") +/// "butterfly" +/// +pub fn append(to first: String, suffix second: String) -> String { + first + |> string_builder.from_string + |> string_builder.append(second) + |> string_builder.to_string +} - /// Creates a new string by joining many strings together. - /// - /// This function copies both strings and runs in linear time. If you find - /// yourself joining strings frequently consider using the [string_builder](../string_builder) - /// module as it can append strings much faster! - /// - /// ## Examples - /// - /// > concat(["never", "the", "less"]) - /// "nevertheless" - /// - pub fn concat(strings: List(String)) -> String { - strings - |> string_builder.from_strings - |> string_builder.to_string - } +/// Creates a new string by joining many strings together. +/// +/// This function copies both strings and runs in linear time. If you find +/// yourself joining strings frequently consider using the [string_builder](../string_builder) +/// module as it can append strings much faster! +/// +/// ## Examples +/// +/// > concat(["never", "the", "less"]) +/// "nevertheless" +/// +pub fn concat(strings: List(String)) -> String { + strings + |> string_builder.from_strings + |> string_builder.to_string +} +if erlang { /// Creates a new string by repeating a string a given number of times. /// /// This function runs in linear time. diff --git a/src/gleam/string_builder.gleam b/src/gleam/string_builder.gleam index 707521e..f39ce49 100644 --- a/src/gleam/string_builder.gleam +++ b/src/gleam/string_builder.gleam @@ -11,56 +11,93 @@ /// pub external type StringBuilder +/// Prepends a String onto the start of some StringBuilder. +/// +/// Runs in constant time. +/// +pub fn prepend( + to builder: StringBuilder, + prefix prefix: String, +) -> StringBuilder { + append_builder(from_string(prefix), builder) +} + +/// Appends a String onto the end of some StringBuilder. +/// +/// Runs in constant time. +/// +pub fn append(to builder: StringBuilder, suffix second: String) -> StringBuilder { + append_builder(builder, from_string(second)) +} + +/// Prepends some StringBuilder onto the start of another. +/// +/// Runs in constant time. +/// +pub fn prepend_builder( + to builder: StringBuilder, + prefix prefix: StringBuilder, +) -> StringBuilder { + do_append(prefix, builder) +} + +/// Appends some StringBuilder onto the end of another. +/// +/// Runs in constant time. +/// +pub fn append_builder( + to builder: StringBuilder, + suffix suffix: StringBuilder, +) -> StringBuilder { + do_append(builder, suffix) +} + if erlang { - /// Prepends a String onto the start of some StringBuilder. - /// - /// Runs in constant time. - /// - pub external fn prepend(to: StringBuilder, prefix: String) -> StringBuilder = - "gleam_stdlib" "iodata_prepend" - - /// Appends a String onto the end of some StringBuilder. - /// - /// Runs in constant time. - /// - pub external fn append(to: StringBuilder, suffix: String) -> StringBuilder = + external fn do_append(StringBuilder, StringBuilder) -> StringBuilder = "gleam_stdlib" "iodata_append" +} - /// Prepends some StringBuilder onto the start of another. - /// - /// Runs in constant time. - /// - pub external fn prepend_builder( - to: StringBuilder, - prefix: StringBuilder, - ) -> StringBuilder = - "gleam_stdlib" "iodata_prepend" - - /// Appends some StringBuilder onto the end of another. - /// - /// Runs in constant time. - /// - pub external fn append_builder( - to: StringBuilder, - suffix: StringBuilder, - ) -> StringBuilder = - "gleam_stdlib" "iodata_append" +if javascript { + external fn do_append(StringBuilder, StringBuilder) -> StringBuilder = + "../gleam_stdlib.js" "add" +} - /// Converts a list of strings into a builder. - /// - /// Runs in constant time. - /// - pub external fn from_strings(List(String)) -> StringBuilder = +/// Converts a list of strings into a builder. +/// +/// Runs in constant time. +/// +pub fn from_strings(strings: List(String)) -> StringBuilder { + do_from_strings(strings) +} + +if erlang { + external fn do_from_strings(List(String)) -> StringBuilder = "gleam_stdlib" "identity" +} + +if javascript { + external fn do_from_strings(List(String)) -> StringBuilder = + "../gleam_stdlib.js" "join" +} - /// Joins a list of builders into a single builder. - /// - /// Runs in constant time. - /// - pub external fn concat(List(StringBuilder)) -> StringBuilder = +/// Joins a list of builders into a single builder. +/// +/// Runs in constant time. +/// +pub fn concat(builders: List(StringBuilder)) -> StringBuilder { + do_concat(builders) +} + +if erlang { + external fn do_concat(List(StringBuilder)) -> StringBuilder = "gleam_stdlib" "identity" } +if javascript { + external fn do_concat(List(StringBuilder)) -> StringBuilder = + "../gleam_stdlib.js" "join" +} + /// Converts a string into a builder. /// /// Runs in constant time. @@ -98,30 +135,72 @@ if javascript { "../gleam_stdlib.js" "identity" } +/// Returns the size of the StringBuilder in bytes. +/// +pub fn byte_size(builder: StringBuilder) -> Int { + do_byte_size(builder) +} + if erlang { - /// Returns the size of the StringBuilder in bytes. - /// - pub external fn byte_size(StringBuilder) -> Int = + external fn do_byte_size(StringBuilder) -> Int = "erlang" "iolist_size" +} + +if javascript { + external fn do_byte_size(StringBuilder) -> Int = + "../gleam_stdlib.js" "byte_size" +} - /// Creates a builder containing the textual representation of a given float. - /// - pub external fn from_float(Float) -> StringBuilder = +/// Creates a builder containing the textual representation of a given float. +/// +pub fn from_float(f: Float) -> StringBuilder { + do_from_float(f) +} + +if erlang { + external fn do_from_float(Float) -> StringBuilder = "io_lib_format" "fwrite_g" +} + +if javascript { + external fn do_from_float(Float) -> StringBuilder = + "../gleam_stdlib.js" "to_string" +} + +/// Converts a builder to a new builder where the contents have been +/// lowercased. +/// +pub fn lowercase(builder: StringBuilder) -> StringBuilder { + do_lowercase(builder) +} - /// Converts a builder to a new builder where the contents have been - /// lowercased. - /// - pub external fn lowercase(StringBuilder) -> StringBuilder = +if erlang { + external fn do_lowercase(StringBuilder) -> StringBuilder = "string" "lowercase" +} + +if javascript { + external fn do_lowercase(StringBuilder) -> StringBuilder = + "../gleam_stdlib.js" "lowercase" +} - /// Converts a builder to a new builder where the contents have been - /// uppercased. - /// - pub external fn uppercase(StringBuilder) -> StringBuilder = +/// Converts a builder to a new builder where the contents have been +/// uppercased. +/// +pub fn uppercase(builder: StringBuilder) -> StringBuilder { + do_uppercase(builder) +} + +if erlang { + external fn do_uppercase(StringBuilder) -> StringBuilder = "string" "uppercase" } +if javascript { + external fn do_uppercase(StringBuilder) -> StringBuilder = + "../gleam_stdlib.js" "uppercase" +} + /// Converts a builder to a new builder with the contents reversed. /// pub fn reverse(builder: StringBuilder) -> StringBuilder { @@ -138,6 +217,12 @@ if javascript { "../gleam_stdlib.js" "string_reverse" } +/// Splits a builder on a given pattern into a list of builders. +/// +pub fn split(iodata: StringBuilder, on pattern: String) -> List(StringBuilder) { + do_split(iodata, pattern) +} + if erlang { type Direction { All @@ -146,13 +231,19 @@ if erlang { external fn erl_split(StringBuilder, String, Direction) -> List(StringBuilder) = "string" "split" - /// Splits a builder on a given pattern into a list of builders. - /// - pub fn split(iodata: StringBuilder, on pattern: String) -> List(StringBuilder) { - erl_split(iodata, pattern, All) + fn do_split(iodata: StringBuilder, pattern: String) -> List(StringBuilder) { + do_split(iodata, pattern, All) } } +if javascript { + external fn do_split( + builder: StringBuilder, + pattern: String, + ) -> List(StringBuilder) = + "../gleam_stdlib.js" "split" +} + /// Replaces all instances of a pattern with a given string substitute. /// pub fn replace( @@ -186,39 +277,60 @@ if javascript { "../gleam_stdlib.js" "string_replace" } +/// Compares two builders to determine if they have the same textual content. +/// +/// Comparing two iodata using the `==` operator may return False even if they +/// have the same content as they may have been build in different ways, so +/// using this function is often preferred. +/// +/// ## Examples +/// +/// > from_strings(["a", "b"]) == new("ab") +/// False +/// +/// > is_equal(from_strings(["a", "b"]), new("ab")) +/// True +/// +/// +pub fn is_equal(a: StringBuilder, b: StringBuilder) -> Bool { + do_is_equal(a, b) +} + if erlang { - /// Compares two builders to determine if they have the same textual content. - /// - /// Comparing two iodata using the `==` operator may return False even if they - /// have the same content as they may have been build in different ways, so - /// using this function is often preferred. - /// - /// ## Examples - /// - /// > from_strings(["a", "b"]) == new("ab") - /// False - /// - /// > is_equal(from_strings(["a", "b"]), new("ab")) - /// True - /// - /// - pub external fn is_equal(StringBuilder, StringBuilder) -> Bool = + external fn do_is_equal(StringBuilder, StringBuilder) -> Bool = "string" "equal" +} + +if javascript { + external fn do_is_equal(StringBuilder, StringBuilder) -> Bool = + "../gleam_stdlib.js" "equal" +} - /// Inspects a builder to determine if it is equivalent to an empty string. - /// - /// ## Examples - /// - /// > new("ok") |> is_empty - /// False - /// - /// > new("") |> is_empty - /// True - /// - /// > from_strings([]) |> is_empty - /// True - /// - /// - pub external fn is_empty(StringBuilder) -> Bool = +/// Inspects a builder to determine if it is equivalent to an empty string. +/// +/// ## Examples +/// +/// > new("ok") |> is_empty +/// False +/// +/// > new("") |> is_empty +/// True +/// +/// > from_strings([]) |> is_empty +/// True +/// +/// +pub fn is_empty(builder: StringBuilder) -> Bool { + do_is_empty(builder) +} + +if erlang { + external fn do_is_empty(StringBuilder) -> Bool = "string" "is_empty" } + +if javascript { + fn do_is_empty(builder: StringBuilder) -> Bool { + from_string("") == builder + } +} diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl index fe70eea..836bdbe 100644 --- a/src/gleam_stdlib.erl +++ b/src/gleam_stdlib.erl @@ -3,16 +3,16 @@ -export([should_equal/2, should_not_equal/2, should_be_ok/1, should_be_error/1, atom_from_string/1, atom_create_from_string/1, atom_to_string/1, - map_get/2, iodata_append/2, iodata_prepend/2, identity/1, - decode_int/1, decode_bool/1, decode_float/1, - decode_thunk/1, decode_atom/1, decode_list/1, decode_field/2, - decode_element/2, parse_int/1, parse_float/1, compare_strings/2, - string_pop_grapheme/1, string_starts_with/2, string_ends_with/2, - string_pad/4, decode_tuple2/1, decode_tuple3/1, decode_tuple4/1, - decode_tuple5/1, decode_tuple6/1, decode_map/1, bit_string_int_to_u32/1, - bit_string_int_from_u32/1, bit_string_append/2, bit_string_part_/3, - decode_bit_string/1, compile_regex/2, regex_match/2, regex_split/2, - regex_scan/2, base_decode64/1, wrap_list/1, rescue/1, get_line/1]). + map_get/2, iodata_append/2, identity/1, decode_int/1, decode_bool/1, + decode_float/1, decode_thunk/1, decode_atom/1, decode_list/1, + decode_field/2, decode_element/2, parse_int/1, parse_float/1, + less_than/2, string_pop_grapheme/1, string_starts_with/2, + string_ends_with/2, string_pad/4, decode_tuple2/1, decode_tuple3/1, + decode_tuple4/1, decode_tuple5/1, decode_tuple6/1, decode_map/1, + bit_string_int_to_u32/1, bit_string_int_from_u32/1, + bit_string_append/2, bit_string_part_/3, decode_bit_string/1, + compile_regex/2, regex_match/2, regex_split/2, regex_scan/2, + base_decode64/1, wrap_list/1, rescue/1, get_line/1]). should_equal(Actual, Expected) -> ?assertEqual(Expected, Actual), @@ -45,7 +45,6 @@ atom_from_string(S) -> end. iodata_append(Iodata, String) -> [Iodata, String]. -iodata_prepend(Iodata, String) -> [String, Iodata]. identity(X) -> X. @@ -132,12 +131,8 @@ parse_float(String) -> _ -> {error, nil} end. -compare_strings(Lhs, Rhs) -> - if - Lhs == Rhs -> eq; - Lhs < Rhs -> lt; - true -> gt - end. +less_than(Lhs, Rhs) -> + Lhs < Rhs. string_starts_with(_, <<>>) -> true; string_starts_with(String, Prefix) when byte_size(Prefix) > byte_size(String) -> false; @@ -228,14 +223,6 @@ base_decode64(S) -> wrap_list(X) when is_list(X) -> X; wrap_list(X) -> [X]. -rescue(F) -> - try {ok, F()} - catch - throw:X -> {error, {thrown, X}}; - error:X -> {error, {errored, X}}; - exit:X -> {error, {exited, X}} - end. - get_line(Prompt) -> case io:get_line(Prompt) of eof -> {error, eof}; diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js index 15c8a09..30a2585 100644 --- a/src/gleam_stdlib.js +++ b/src/gleam_stdlib.js @@ -1,3 +1,11 @@ +function to_list(array) { + let list = []; + for (let item of array.reverse()) { + list = [item, list]; + } + return list; +} + export function identity(x) { return x; } @@ -10,7 +18,7 @@ export function parse_int(value) { } } -export function int_to_string(int) { +export function to_string(int) { return int.toString(); } @@ -38,10 +46,40 @@ export function string_length(string) { } } -export function string_lowercase(string) { +export function lowercase(string) { return string.toLowerCase(); } -export function string_uppercase(string) { +export function uppercase(string) { return string.toUpperCase(); } + +export function less_than(a, b) { + return a < b; +} + +export function add(a, b) { + return a + b; +} + +export function equal(a, b) { + return a === b; +} + +export function split(xs, pattern) { + return to_list(xs.split(pattern)); +} + +export function join(xs) { + return xs.flat().join(""); +} + +export function byte_size(data) { + if (typeof Blob === "function") { + return new Blob([data]).size; + } else if (typeof Buffer === "function") { + return Buffer.byteLength(data); + } else { + return data.length; + } +} |