diff options
author | Louis Pilfold <louis@lpil.uk> | 2020-06-26 17:47:09 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2020-06-30 12:14:27 +0100 |
commit | c5f596770a20ac4afdd3c7fd37bfb8550605cbe9 (patch) | |
tree | ad1897084ba811e8970d2a3a33d7d59082a9897d | |
parent | d90199e49c75d051853af52156ee8adaddc58564 (diff) | |
download | gleam_stdlib-c5f596770a20ac4afdd3c7fd37bfb8550605cbe9.tar.gz gleam_stdlib-c5f596770a20ac4afdd3c7fd37bfb8550605cbe9.zip |
iodata -> string_builder
-rw-r--r-- | src/gleam/float.gleam | 6 | ||||
-rw-r--r-- | src/gleam/iodata.gleam | 159 | ||||
-rw-r--r-- | src/gleam/string.gleam | 38 | ||||
-rw-r--r-- | src/gleam/string_builder.gleam | 168 | ||||
-rw-r--r-- | test/gleam/iodata_test.gleam | 96 | ||||
-rw-r--r-- | test/gleam/string_builder_test.gleam | 105 |
6 files changed, 295 insertions, 277 deletions
diff --git a/src/gleam/float.gleam b/src/gleam/float.gleam index 953a267..f810161 100644 --- a/src/gleam/float.gleam +++ b/src/gleam/float.gleam @@ -1,4 +1,4 @@ -import gleam/iodata +import gleam/string_builder import gleam/order.{Order} pub type Float = @@ -25,8 +25,8 @@ pub external fn parse(String) -> Result(Float, Nil) = /// pub fn to_string(f: Float) -> String { f - |> iodata.from_float - |> iodata.to_string + |> string_builder.from_float + |> string_builder.to_string } /// Compares two floats, returning an order. diff --git a/src/gleam/iodata.gleam b/src/gleam/iodata.gleam deleted file mode 100644 index fe24974..0000000 --- a/src/gleam/iodata.gleam +++ /dev/null @@ -1,159 +0,0 @@ -/// Iodata is a type used for efficiently building strings. -/// -/// When we append one string to another the strings must be copied to a -/// new location in memory so that they can sit together. This behaviour -/// enables efficient reading of the string but copying can be expensive, -/// especially if we want to join many strings together. -/// -/// Iodata is different in that it can be joined together in constant time -/// using minimal memory, and then can be efficiently converted to a string -/// using the `to_string` function. -/// -pub external type Iodata - -/// Prepend a String onto the start of some Iodata. -/// -/// Runs in constant time. -/// -pub external fn prepend(to: Iodata, prefix: String) -> Iodata = - "gleam_stdlib" "iodata_prepend" - -/// Append a String onto the end of some Iodata. -/// -/// Runs in constant time. -/// -pub external fn append(to: Iodata, suffix: String) -> Iodata = - "gleam_stdlib" "iodata_append" - -/// Prepend some Iodata onto the start of another. -/// -/// Runs in constant time. -/// -pub external fn prepend_iodata(to: Iodata, prefix: Iodata) -> Iodata = - "gleam_stdlib" "iodata_prepend" - -/// Append some Iodata onto the end of another. -/// -/// Runs in constant time. -/// -pub external fn append_iodata(to: Iodata, suffix: Iodata) -> Iodata = - "gleam_stdlib" "iodata_append" - -/// Convert a list of strings into iodata. -/// -/// Runs in constant time. -/// -pub external fn from_strings(List(String)) -> Iodata = - "gleam_stdlib" "identity" - -/// Joins a list of iodata into a single iodata. -/// -/// Runs in constant time. -/// -pub external fn concat(List(Iodata)) -> Iodata = - "gleam_stdlib" "identity" - -/// Convert a string into iodata. -/// -/// Runs in constant time. -/// -pub external fn new(String) -> Iodata = - "gleam_stdlib" "identity" - -/// Turns an `Iodata` into a `String` -/// -/// This function is implemented natively by the virtual machine and is highly -/// optimised. -/// -pub external fn to_string(Iodata) -> String = - "erlang" "iolist_to_binary" - -/// Returns the size of the Iodata in bytes. -/// -pub external fn byte_size(Iodata) -> Int = - "erlang" "iolist_size" - -/// Creates textual representation of the given float as iodata. -/// -pub external fn from_float(Float) -> Iodata = - "io_lib_format" "fwrite_g" - -/// Converts Iodata to a new Iodata where valid UTF-8 string data is -/// lowercased. -/// -pub external fn lowercase(Iodata) -> Iodata = - "string" "lowercase" - -/// Converts Iodata to a new Iodata where valid UTF-8 string data is -/// uppercased. -/// -pub external fn uppercase(Iodata) -> Iodata = - "string" "uppercase" - -/// Converts Iodata to a new Iodata where valid UTF-8 string data is -/// reversed. -/// -pub external fn reverse(Iodata) -> Iodata = - "string" "reverse" - -type Direction { - All -} - -external fn erl_split(Iodata, String, Direction) -> List(Iodata) = - "string" "split" - -/// Splits iodata on a given pattern into a list of iodata. -/// -pub fn split(iodata: Iodata, on pattern: String) -> List(Iodata) { - erl_split(iodata, pattern, All) -} - -external fn erl_replace(Iodata, String, String, Direction) -> Iodata = - "string" "replace" - -/// Replaces all instances of a pattern with a given string substitute. -/// -pub fn replace( - in iodata: Iodata, - each pattern: String, - with substitute: String, -) -> Iodata { - erl_replace(iodata, pattern, substitute, All) -} - -/// Compare two pieces of iodata 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(Iodata, Iodata) -> Bool = - "string" "equal" - -/// Inspect some iodata 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(Iodata) -> Bool = - "string" "is_empty" diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index 740f90b..1714dad 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -1,7 +1,7 @@ //// Strings in Gleam are UTF-8 binaries. They can be written in your code a //// text surrounded by `"double quotes"`. -import gleam/iodata +import gleam/string_builder import gleam/dynamic.{Dynamic} import gleam/list import gleam/order @@ -56,9 +56,9 @@ pub external fn length(String) -> Int = /// pub fn reverse(string: String) -> String { string - |> iodata.new - |> iodata.reverse - |> iodata.to_string + |> string_builder.from_string + |> string_builder.reverse + |> string_builder.to_string } /// Create a new string by replacing all occurrences of a given substring. @@ -77,9 +77,9 @@ pub fn replace( with substitute: String, ) -> String { string - |> iodata.new - |> iodata.replace(each: pattern, with: substitute) - |> iodata.to_string + |> string_builder.from_string + |> string_builder.replace(each: pattern, with: substitute) + |> string_builder.to_string } /// Create a new string with all the graphemes in the input string converted to @@ -243,9 +243,9 @@ pub external fn ends_with(String, String) -> Bool = /// pub fn split(x: String, on substring: String) -> List(String) { x - |> iodata.new - |> iodata.split(on: substring) - |> list.map(with: iodata.to_string) + |> string_builder.from_string + |> string_builder.split(on: substring) + |> list.map(with: string_builder.to_string) } external fn erl_split(String, String) -> List(String) = @@ -276,7 +276,7 @@ pub fn split_once( /// Create 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 [iodata](../iodata) +/// yourself joining strings frequently consider using the [string_builder](../iodata) /// module as it can append strings much faster! /// /// ## Examples @@ -286,15 +286,15 @@ pub fn split_once( /// pub fn append(to first: String, suffix second: String) -> String { first - |> iodata.new - |> iodata.append(second) - |> iodata.to_string + |> string_builder.from_string + |> string_builder.append(second) + |> string_builder.to_string } /// Create 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 [iodata](../iodata) +/// yourself joining strings frequently consider using the [string_builder](../iodata) /// module as it can append strings much faster! /// /// ## Examples @@ -304,8 +304,8 @@ pub fn append(to first: String, suffix second: String) -> String { /// pub fn concat(strings: List(String)) -> String { strings - |> iodata.from_strings - |> iodata.to_string + |> string_builder.from_strings + |> string_builder.to_string } fn repeat_help(chunk: String, result: List(String), repeats: Int) -> String { @@ -340,8 +340,8 @@ pub fn repeat(string: String, times times: Int) -> String { pub fn join(strings: List(String), with separator: String) -> String { strings |> list.intersperse(with: separator) - |> iodata.from_strings - |> iodata.to_string + |> string_builder.from_strings + |> string_builder.to_string } type Direction { diff --git a/src/gleam/string_builder.gleam b/src/gleam/string_builder.gleam new file mode 100644 index 0000000..5acf8e1 --- /dev/null +++ b/src/gleam/string_builder.gleam @@ -0,0 +1,168 @@ +/// StringBuilder is a type used for efficiently building strings. +/// +/// When we append one string to another the strings must be copied to a +/// new location in memory so that they can sit together. This behaviour +/// enables efficient reading of the string but copying can be expensive, +/// especially if we want to join many strings together. +/// +/// StringBuilder is different in that it can be joined together in constant time +/// using minimal memory, and then can be efficiently converted to a string +/// using the `to_string` function. +/// +pub external type StringBuilder + +/// Prepend 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" + +/// Append a String onto the end of some StringBuilder. +/// +/// Runs in constant time. +/// +pub external fn append(to: StringBuilder, suffix: String) -> StringBuilder = + "gleam_stdlib" "iodata_append" + +/// Prepend 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" + +/// Append 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" + +/// Convert a list of strings into a builder. +/// +/// Runs in constant time. +/// +pub external fn from_strings(List(String)) -> StringBuilder = + "gleam_stdlib" "identity" + +/// Joins a list of builders into a single builder. +/// +/// Runs in constant time. +/// +pub external fn concat(List(StringBuilder)) -> StringBuilder = + "gleam_stdlib" "identity" + +/// Convert a string into a builder. +/// +/// Runs in constant time. +/// +pub external fn from_string(String) -> StringBuilder = + "gleam_stdlib" "identity" + +/// Turns an `StringBuilder` into a `String` +/// +/// This function is implemented natively by the virtual machine and is highly +/// optimised. +/// +pub external fn to_string(StringBuilder) -> String = + "erlang" "iolist_to_binary" + +/// Returns the size of the StringBuilder in bytes. +/// +pub external fn byte_size(StringBuilder) -> Int = + "erlang" "iolist_size" + +/// Creates a builder containing the textual representation of a given float. +/// +pub external fn from_float(Float) -> StringBuilder = + "io_lib_format" "fwrite_g" + +/// Converts a builder to a new builder where the contents have been +/// lowercased. +/// +pub external fn lowercase(StringBuilder) -> StringBuilder = + "string" "lowercase" + +/// Converts a builder to a new builder where the contents have been +/// uppercased. +/// +pub external fn uppercase(StringBuilder) -> StringBuilder = + "string" "uppercase" + +/// Converts a builder to a new builder with the contents reversed. +/// +pub external fn reverse(StringBuilder) -> StringBuilder = + "string" "reverse" + +type Direction { + All +} + +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) +} + +external fn erl_replace( + StringBuilder, + String, + String, + Direction, +) -> StringBuilder = + "string" "replace" + +/// Replaces all instances of a pattern with a given string substitute. +/// +pub fn replace( + in iodata: StringBuilder, + each pattern: String, + with substitute: String, +) -> StringBuilder { + erl_replace(iodata, pattern, substitute, All) +} + +/// Compare 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 = + "string" "equal" + +/// Inspect 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 = + "string" "is_empty" diff --git a/test/gleam/iodata_test.gleam b/test/gleam/iodata_test.gleam deleted file mode 100644 index bc4fe98..0000000 --- a/test/gleam/iodata_test.gleam +++ /dev/null @@ -1,96 +0,0 @@ -import gleam/should -import gleam/iodata - -pub fn iodata_test() { - let data = iodata.new("ello") - |> iodata.append(",") - |> iodata.append(" world!") - |> iodata.prepend("H") - - data - |> iodata.to_string - |> should.equal("Hello, world!") - - data - |> iodata.byte_size - |> should.equal(13) - - let data = iodata.new("ello") - |> iodata.append_iodata(iodata.new(",")) - |> iodata.append_iodata( - iodata.concat([iodata.new(" wo"), iodata.new("rld!")]), - ) - |> iodata.prepend_iodata(iodata.new("H")) - - data - |> iodata.to_string - |> should.equal("Hello, world!") - - data - |> iodata.byte_size - |> should.equal(13) -} - -pub fn lowercase_test() { - ["Gleam", "Gleam"] - |> iodata.from_strings - |> iodata.lowercase - |> iodata.to_string - |> should.equal("gleamgleam") -} - -pub fn uppercase_test() { - ["Gleam", "Gleam"] - |> iodata.from_strings - |> iodata.uppercase - |> iodata.to_string - |> should.equal("GLEAMGLEAM") -} - -pub fn split_test() { - "Gleam,Erlang,Elixir" - |> iodata.new - |> iodata.split(",") - |> should.equal( - [iodata.new("Gleam"), iodata.new("Erlang"), iodata.new("Elixir")], - ) - - ["Gleam, Erl", "ang,Elixir"] - |> iodata.from_strings - |> iodata.split(", ") - |> should.equal( - [iodata.new("Gleam"), iodata.from_strings(["Erl", "ang,Elixir"])], - ) -} - -pub fn is_equal_test() { - iodata.new("12") - |> iodata.is_equal(iodata.from_strings(["1", "2"])) - |> should.be_true - - iodata.new("12") - |> iodata.is_equal(iodata.new("12")) - |> should.be_true - - iodata.new("12") - |> iodata.is_equal(iodata.new("2")) - |> should.be_false -} - -pub fn is_empty_test() { - iodata.new("") - |> iodata.is_empty - |> should.be_true - - iodata.new("12") - |> iodata.is_empty - |> should.be_false - - iodata.from_strings([]) - |> iodata.is_empty - |> should.be_true - - iodata.from_strings(["", ""]) - |> iodata.is_empty - |> should.be_true -} diff --git a/test/gleam/string_builder_test.gleam b/test/gleam/string_builder_test.gleam new file mode 100644 index 0000000..b2e30cf --- /dev/null +++ b/test/gleam/string_builder_test.gleam @@ -0,0 +1,105 @@ +import gleam/should +import gleam/string_builder + +pub fn string_builder_test() { + let data = string_builder.from_string("ello") + |> string_builder.append(",") + |> string_builder.append(" world!") + |> string_builder.prepend("H") + + data + |> string_builder.to_string + |> should.equal("Hello, world!") + + data + |> string_builder.byte_size + |> should.equal(13) + + let data = string_builder.from_string("ello") + |> string_builder.append_builder(string_builder.from_string(",")) + |> string_builder.append_builder( + string_builder.concat( + [string_builder.from_string(" wo"), string_builder.from_string("rld!")], + ), + ) + |> string_builder.prepend_builder(string_builder.from_string("H")) + + data + |> string_builder.to_string + |> should.equal("Hello, world!") + + data + |> string_builder.byte_size + |> should.equal(13) +} + +pub fn lowercase_test() { + ["Gleam", "Gleam"] + |> string_builder.from_strings + |> string_builder.lowercase + |> string_builder.to_string + |> should.equal("gleamgleam") +} + +pub fn uppercase_test() { + ["Gleam", "Gleam"] + |> string_builder.from_strings + |> string_builder.uppercase + |> string_builder.to_string + |> should.equal("GLEAMGLEAM") +} + +pub fn split_test() { + "Gleam,Erlang,Elixir" + |> string_builder.from_string + |> string_builder.split(",") + |> should.equal( + [ + string_builder.from_string("Gleam"), + string_builder.from_string("Erlang"), + string_builder.from_string("Elixir"), + ], + ) + + ["Gleam, Erl", "ang,Elixir"] + |> string_builder.from_strings + |> string_builder.split(", ") + |> should.equal( + [ + string_builder.from_string("Gleam"), + string_builder.from_strings(["Erl", "ang,Elixir"]), + ], + ) +} + +pub fn is_equal_test() { + string_builder.from_string("12") + |> string_builder.is_equal(string_builder.from_strings(["1", "2"])) + |> should.be_true + + string_builder.from_string("12") + |> string_builder.is_equal(string_builder.from_string("12")) + |> should.be_true + + string_builder.from_string("12") + |> string_builder.is_equal(string_builder.from_string("2")) + |> should.be_false +} + +pub fn is_empty_test() { + string_builder.from_string("") + |> string_builder.is_empty + |> should.be_true + + string_builder.from_string("12") + |> string_builder.is_empty + |> should.be_false + + string_builder.from_strings([]) + |> string_builder.is_empty + |> should.be_true + + string_builder.from_strings(["", ""]) + |> string_builder.is_empty + |> should.be_true +} |