diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gleam/atom.gleam | 64 | ||||
-rw-r--r-- | src/gleam/dynamic.gleam | 73 | ||||
-rw-r--r-- | src/gleam/io.gleam | 75 | ||||
-rw-r--r-- | src/gleam/string.gleam | 4 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 45 | ||||
-rw-r--r-- | src/gleam_stdlib.js | 8 |
6 files changed, 109 insertions, 160 deletions
diff --git a/src/gleam/atom.gleam b/src/gleam/atom.gleam deleted file mode 100644 index 2d96053..0000000 --- a/src/gleam/atom.gleam +++ /dev/null @@ -1,64 +0,0 @@ -if erlang { - /// Atom is a special string-like data-type that is most commonly used for - /// interfacing with code written in other BEAM languages such as Erlang and - /// Elixir. It is preferable to define your own custom types to use instead of - /// atoms where possible. - /// - /// Atoms are not used much in typical Gleam code! - /// - /// ## Creating atoms - /// - /// We can create atoms with the the [`create_from_string`](#create_from_string) - /// function, though we must be careful when doing so as atoms are never - /// garbage collected. If we create too many atoms (for example, if we convert - /// user input into atoms) we may hit the max limit of atoms and cause the - /// virtual machine to crash. - /// - pub external type Atom - - /// An error returned when no atom is found in the virtual machine's atom table - /// for a given string when calling the [`from_string`](#from_string) function. - pub type FromStringError { - AtomNotLoaded - } - - /// Finds an existing Atom for the given String. - /// - /// If no atom is found in the virtual machine's atom table for the String then - /// an error is returned. - /// - /// ## Examples - /// - /// > from_string("ok") - /// Ok(create_from_string("ok")) - /// - /// > from_string("some_new_atom") - /// Error(AtomNotLoaded) - /// - pub external fn from_string(String) -> Result(Atom, FromStringError) = - "gleam_stdlib" "atom_from_string" - - /// Creates an atom from a string, inserting a new value into the virtual - /// machine's atom table if an atom does not already exist for the given - /// string. - /// - /// We must be careful when using this function as there is a limit to the - /// number of atom that can fit in the virtual machine's atom table. Never - /// convert user input into atoms as filling the atom table will cause the - /// virtual machine to crash! - /// - pub external fn create_from_string(String) -> Atom = - "gleam_stdlib" "atom_create_from_string" - - /// Retuns a `String` corresponding to the text representation of the given - /// `Atom`. - /// - /// ## Examples - /// - /// > let ok_atom = create_from_string("ok") - /// > to_string(ok_atom) - /// "ok" - /// - pub external fn to_string(Atom) -> String = - "gleam_stdlib" "atom_to_string" -} diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam index 8516b40..a51fd0e 100644 --- a/src/gleam/dynamic.gleam +++ b/src/gleam/dynamic.gleam @@ -1,9 +1,8 @@ if erlang { - import gleam/atom import gleam/bit_string import gleam/list import gleam/map.{Map} - import gleam/option.{None, Option, Some} + import gleam/option.{Option} import gleam/result import gleam/string_builder @@ -93,21 +92,6 @@ if erlang { pub external fn float(from: Dynamic) -> Result(Float, String) = "gleam_stdlib" "decode_float" - /// Checks to see whether a Dynamic value is an atom, and return the atom if - /// it is. - /// - /// ## Examples - /// - /// > import gleam/atom - /// > atom(from(atom.create_from_string("hello"))) - /// OK("hello") - /// - /// > atom(from(123)) - /// Error("Expected an Atom, got `123`") - /// - pub external fn atom(from: Dynamic) -> Result(atom.Atom, String) = - "gleam_stdlib" "decode_atom" - /// Checks to see whether a Dynamic value is an bool, and return the bool if /// it is. /// @@ -156,7 +140,7 @@ if erlang { "gleam_stdlib" "decode_list" /// Checks to see whether a Dynamic value is a result, and return the result if - /// it is + /// it is. /// /// ## Examples /// @@ -169,24 +153,8 @@ if erlang { /// > result(from(123)) /// Error("Expected a 2 element tuple, got an int") /// - pub fn result(from: Dynamic) -> Result(Result(Dynamic, Dynamic), String) { - try #(key, val) = tuple2(from) - - try tag = atom(key) - let ok_atom = atom.create_from_string("ok") - let error_atom = atom.create_from_string("error") - case tag { - tag if tag == ok_atom -> Ok(Ok(val)) - tag if tag == error_atom -> Ok(Error(val)) - tag -> - "Expected a tag of \"ok\" or \"error\", got \"" - |> string_builder.from_string - |> string_builder.append(atom.to_string(tag)) - |> string_builder.append("\"") - |> string_builder.to_string - |> Error - } - } + pub external fn result(Dynamic) -> Result(Result(Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_result" /// Checks to see whether a Dynamic value is a result of a particular type, and /// return the result if it is @@ -254,35 +222,34 @@ if erlang { |> result.then(list.try_map(_, decoder_type)) } - /// Checks to see if a Dynamic value is an Option of a particular type, and return - /// the Option if it is. - /// - /// The second argument is a decoder function used to decode the elements of - /// the list. The list is only decoded if all elements in the list can be - /// successfully decoded using this function. + /// Checks to see if a Dynamic value is a nullable version of a particular + /// type, and return the Option if it is. /// /// ## Examples /// /// > option(from("Hello"), string) /// Ok(Some("Hello")) /// + /// > option(from("Hello"), string) + /// Ok(Some("Hello")) + /// /// > option(from(atom.from_string("null")), string) /// Ok(None) /// + /// > option(from(atom.from_string("nil")), string) + /// Ok(None) + /// + /// > option(from(atom.from_string("undefined")), string) + /// Ok(None) + /// /// > option(from(123), string) /// Error("Expected a bit_string, got an int") /// - pub fn option( - from dynamic: Dynamic, - of decoder: Decoder(inner), - ) -> Result(Option(inner), String) { - let Ok(null) = atom.from_string("null") - case atom(dynamic), decoder(dynamic) { - Ok(atom), _ if atom == null -> Ok(None) - _, Ok(result) -> Ok(Some(result)) - _, Error(msg) -> Error(msg) - } - } + pub external fn optional( + from: Dynamic, + of: Decoder(inner), + ) -> Result(Option(inner), String) = + "gleam_stdlib" "decode_optional" /// Checks to see if a Dynamic value is a map with a specific field, and return /// the value of the field if it is. diff --git a/src/gleam/io.gleam b/src/gleam/io.gleam index 9482843..4b61eec 100644 --- a/src/gleam/io.gleam +++ b/src/gleam/io.gleam @@ -1,36 +1,55 @@ -if erlang { - external type DoNotLeak - - external fn erl_print(String, List(a)) -> DoNotLeak = - "io" "fwrite" +/// Writes a string to standard output. +/// +/// If you want your output to be printed on its own line see `println`. +/// +/// ## Example +/// +/// ``` +/// > io.print("Hi mum") +/// // -> Hi mum +/// Nil +/// ``` +/// +pub fn print(string: String) -> Nil { + do_print(string) +} - /// Writes a string to standard output. - /// - /// ## Example - /// - /// > io.print("Hi mum") - /// // -> Hi mum - /// Nil - /// - pub fn print(string: String) -> Nil { +if erlang { + fn do_print(string: String) -> Nil { erl_print(string, []) Nil } +} - /// Writes a string to standard output, appending a newline to the end. - /// - /// ## Example - /// - /// > io.println("Hi mum") - /// // -> Hi mum - /// Nil - /// - pub fn println(string: String) -> Nil { +if javascript { + external fn do_print(String) -> Nil = + "../gleam_stdlib.js" "print" +} + +/// Writes a string to standard output, appending a newline to the end. +/// +/// ## Example +/// +/// > io.println("Hi mum") +/// // -> Hi mum +/// Nil +/// +pub fn println(string: String) -> Nil { + do_println(string) +} + +if erlang { + fn do_println(string: String) -> Nil { erl_print("~ts\n", [string]) Nil } } +if javascript { + external fn do_println(String) -> Nil = + "../gleam_stdlib.js" "log" +} + /// Prints a value to standard output using Erlang syntax. /// /// The value is returned after being printed so it can be used in pipelines. @@ -66,7 +85,7 @@ if erlang { if javascript { external fn debug_print(anything) -> Nil = - "../gleam_stdlib" "log" + "../gleam_stdlib.js" "log" } if erlang { @@ -77,6 +96,7 @@ if erlang { NoData } + // TODO: move to OS library /// Reads a line from standard input with the given prompt. /// /// # Example @@ -88,3 +108,10 @@ if erlang { pub external fn get_line(prompt: String) -> Result(String, GetLineError) = "gleam_stdlib" "get_line" } + +if erlang { + external type DoNotLeak + + external fn erl_print(String, List(a)) -> DoNotLeak = + "io" "fwrite" +} diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index 7cf506b..545d1e4 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -296,8 +296,8 @@ if erlang { fn do_contains(haystack: String, needle: String) -> Bool { haystack |> erl_contains(needle) - |> dynamic.atom - |> result.is_error + |> dynamic.bit_string + |> result.is_ok } } diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl index 49a3b0c..ff8b97b 100644 --- a/src/gleam_stdlib.erl +++ b/src/gleam_stdlib.erl @@ -2,14 +2,13 @@ -include_lib("eunit/include/eunit.hrl"). -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, identity/1, decode_int/1, decode_bool/1, - decode_float/1, decode_thunk/1, decode_atom/1, decode_list/1, + decode_float/1, decode_thunk/1, decode_list/1, decode_optional/2, 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_int_to_u32/1, bit_string_int_from_u32/1, decode_result/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, get_line/1]). @@ -33,17 +32,6 @@ map_get(Map, Key) -> OkFound -> OkFound end. -atom_create_from_string(S) -> - binary_to_atom(S, utf8). - -atom_to_string(S) -> - atom_to_binary(S, utf8). - -atom_from_string(S) -> - try {ok, binary_to_existing_atom(S, utf8)} - catch error:badarg -> {error, atom_not_loaded} - end. - iodata_append(Iodata, String) -> [Iodata, String]. identity(X) -> X. @@ -79,9 +67,6 @@ decode_tuple6(Data) -> decode_error_msg("a 6 element tuple", Data). decode_map(Data) when is_map(Data) -> {ok, Data}; decode_map(Data) -> decode_error_msg("a map", Data). -decode_atom(Data) when is_atom(Data) -> {ok, Data}; -decode_atom(Data) -> decode_error_msg("an atom", Data). - decode_bit_string(Data) when is_bitstring(Data) -> {ok, Data}; decode_bit_string(Data) -> decode_error_msg("a bit_string", Data). @@ -119,6 +104,32 @@ decode_element(Data, Position) when is_tuple(Data) -> end; decode_element(Data, _Position) -> decode_error_msg("a tuple", Data). +decode_optional(Term, F) -> + Decode = fun(Inner) -> + case F(Inner) of + {ok, Decoded} -> {ok, {some, Decoded}}; + Error -> Error + end + end, + case Term of + undefined -> {ok, none}; + error -> {ok, none}; + null -> {ok, none}; + none -> {ok, none}; + nil -> {ok, none}; + {some, Inner} -> Decode(Inner); + _ -> Decode(Term) + end. + +decode_result(Term) -> + case Term of + {ok, Inner} -> {ok, {ok, Inner}}; + ok -> {ok, {ok, nil}}; + {error, Inner} -> {ok, {error, Inner}}; + error -> {ok, {error, nil}}; + _ -> decode_error_msg("a result tuple", Term) + end. + parse_int(String) -> case catch binary_to_integer(String) of Int when is_integer(Int) -> {ok, Int}; diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js index 68ab895..1e93901 100644 --- a/src/gleam_stdlib.js +++ b/src/gleam_stdlib.js @@ -199,3 +199,11 @@ export function bit_string_to_string(bit_string) { return gleam_error(undefined); } } + +export function print(string) { + if (typeof process === "object") { + process.stdout.write(string); // We can write without a trailing newline + } else { + console.log(string); // We're in a browser. Newlines are mandated + } +} |