Fork of daniellemaywood.uk/gleam — Wasm codegen work
7.3 kB
213 lines
1%% SPDX-License-Identifier: Apache-2.0
2%% SPDX-FileCopyrightText: 2024 The Gleam contributors
3
4-define(is_lowercase_char(X),
5 (X > 96 andalso X < 123)).
6
7-define(is_underscore_char(X),
8 (X == 95)).
9
10-define(is_digit_char(X),
11 (X > 47 andalso X < 58)).
12
13-define(is_ascii_character(X),
14 (erlang:is_integer(X) andalso X >= 32 andalso X =< 126)).
15
16-define(could_be_record(Tuple),
17 erlang:is_tuple(Tuple) andalso
18 erlang:is_atom(erlang:element(1, Tuple)) andalso
19 erlang:element(1, Tuple) =/= false andalso
20 erlang:element(1, Tuple) =/= true andalso
21 erlang:element(1, Tuple) =/= nil
22).
23-define(is_atom_char(C),
24 (?is_lowercase_char(C) orelse
25 ?is_underscore_char(C) orelse
26 ?is_digit_char(C))
27).
28
29-define(grey, "\e[90m").
30-define(reset_color, "\e[39m").
31
32echo(Value, Message, Line) ->
33 StringLine = erlang:integer_to_list(Line),
34 StringValue = echo@inspect(Value),
35 StringMessage =
36 case Message of
37 nil -> "";
38 M -> [" ", M]
39 end,
40
41 io:put_chars(
42 standard_error,
43 [
44 ?grey, ?FILEPATH, $:, StringLine, ?reset_color, StringMessage, $\n,
45 StringValue, $\n
46 ]
47 ),
48 Value.
49
50echo@inspect(Value) ->
51 case Value of
52 nil -> "Nil";
53 true -> "True";
54 false -> "False";
55 Int when erlang:is_integer(Int) -> erlang:integer_to_list(Int);
56 Float when erlang:is_float(Float) -> io_lib_format:fwrite_g(Float);
57 Binary when erlang:is_binary(Binary) -> inspect@binary(Binary);
58 Bits when erlang:is_bitstring(Bits) -> inspect@bit_array(Bits);
59 Atom when erlang:is_atom(Atom) -> inspect@atom(Atom);
60 List when erlang:is_list(List) -> inspect@list(List);
61 Map when erlang:is_map(Map) -> inspect@map(Map);
62 Record when ?could_be_record(Record) -> inspect@record(Record);
63 Tuple when erlang:is_tuple(Tuple) -> inspect@tuple(Tuple);
64 Function when erlang:is_function(Function) -> inspect@function(Function);
65 Any -> ["//erl(", io_lib:format("~p", [Any]), ")"]
66 end.
67
68inspect@bit_array(Bits) ->
69 Pieces = inspect@bit_array_pieces(Bits, []),
70 Inner = lists:join(", ", lists:reverse(Pieces)),
71 ["<<", Inner, ">>"].
72
73inspect@bit_array_pieces(Bits, Acc) ->
74 case Bits of
75 <<>> ->
76 Acc;
77 <<Byte, Rest/bitstring>> ->
78 inspect@bit_array_pieces(Rest, [erlang:integer_to_binary(Byte) | Acc]);
79 _ ->
80 Size = erlang:bit_size(Bits),
81 <<RemainingBits:Size>> = Bits,
82 SizeString = [":size(", erlang:integer_to_binary(Size), ")"],
83 Piece = [erlang:integer_to_binary(RemainingBits), SizeString],
84 [Piece | Acc]
85 end.
86
87inspect@binary(Binary) ->
88 case inspect@maybe_utf8_string(Binary, <<>>) of
89 {ok, InspectedUtf8String} ->
90 InspectedUtf8String;
91 {error, not_a_utf8_string} ->
92 Segments = [erlang:integer_to_list(X) || <<X>> <= Binary],
93 ["<<", lists:join(", ", Segments), ">>"]
94 end.
95
96inspect@atom(Atom) ->
97 Binary = erlang:atom_to_binary(Atom),
98 case inspect@maybe_gleam_atom(Binary, none, <<>>) of
99 {ok, Inspected} -> Inspected;
100 {error, _} -> ["atom.create(\"", Binary, "\")"]
101 end.
102
103inspect@list(List) ->
104 case inspect@list_loop(List, true) of
105 {charlist, _} -> ["charlist.from_string(\"", erlang:list_to_binary(List), "\")"];
106 {proper, Elements} -> ["[", Elements, "]"];
107 {improper, Elements} -> ["//erl([", Elements, "])"]
108 end.
109
110inspect@map(Map) ->
111 Fields = [
112 [<<"#(">>, echo@inspect(Key), <<", ">>, echo@inspect(Value), <<")">>]
113 || {Key, Value} <- maps:to_list(Map)
114 ],
115 ["dict.from_list([", lists:join(", ", Fields), "])"].
116
117inspect@record(Record) ->
118 [Atom | ArgsList] = Tuple = erlang:tuple_to_list(Record),
119 case inspect@maybe_gleam_atom(Atom, none, <<>>) of
120 {ok, Tag} ->
121 Args = lists:join(", ", lists:map(fun echo@inspect/1, ArgsList)),
122 [Tag, "(", Args, ")"];
123 _ ->
124 inspect@tuple(Tuple)
125 end.
126
127inspect@tuple(Tuple) when erlang:is_tuple(Tuple) ->
128 inspect@tuple(erlang:tuple_to_list(Tuple));
129inspect@tuple(Tuple) ->
130 Elements = lists:map(fun echo@inspect/1, Tuple),
131 ["#(", lists:join(", ", Elements), ")"].
132
133inspect@function(Function) ->
134 {arity, Arity} = erlang:fun_info(Function, arity),
135 ArgsAsciiCodes = lists:seq($a, $a + Arity - 1),
136 Args = lists:join(", ", lists:map(fun(Arg) -> <<Arg>> end, ArgsAsciiCodes)),
137 ["//fn(", Args, ") { ... }"].
138
139inspect@maybe_utf8_string(Binary, Acc) ->
140 case Binary of
141 <<>> ->
142 {ok, <<$", Acc/binary, $">>};
143 <<First/utf8, Rest/binary>> ->
144 Escaped = inspect@escape_grapheme(First),
145 inspect@maybe_utf8_string(Rest, <<Acc/binary, Escaped/binary>>);
146 _ ->
147 {error, not_a_utf8_string}
148 end.
149
150inspect@escape_grapheme(Char) ->
151 case Char of
152 $" -> <<$\\, $">>;
153 $\\ -> <<$\\, $\\>>;
154 $\r -> <<$\\, $r>>;
155 $\n -> <<$\\, $n>>;
156 $\t -> <<$\\, $t>>;
157 $\f -> <<$\\, $f>>;
158 X when X > 126, X < 160 -> inspect@convert_to_u(X);
159 X when X < 32 -> inspect@convert_to_u(X);
160 Other -> <<Other/utf8>>
161 end.
162
163inspect@convert_to_u(Code) ->
164 erlang:list_to_binary(io_lib:format("\\u{~4.16.0B}", [Code])).
165
166inspect@list_loop(List, Ascii) ->
167 case List of
168 [] ->
169 {proper, []};
170 [First] when Ascii andalso ?is_ascii_character(First) ->
171 {charlist, nil};
172 [First] ->
173 {proper, [echo@inspect(First)]};
174 [First | Rest] when erlang:is_list(Rest) ->
175 StillAscii = Ascii andalso ?is_ascii_character(First),
176 {Kind, Inspected} = inspect@list_loop(Rest, StillAscii),
177 {Kind, [echo@inspect(First), ", " | Inspected]};
178 [First | ImproperRest] ->
179 {improper, [echo@inspect(First), " | ", echo@inspect(ImproperRest)]}
180 end.
181
182inspect@maybe_gleam_atom(Atom, PrevChar, Acc) when erlang:is_atom(Atom) ->
183 Binary = erlang:atom_to_binary(Atom),
184 inspect@maybe_gleam_atom(Binary, PrevChar, Acc);
185inspect@maybe_gleam_atom(Atom, PrevChar, Acc) ->
186 case {Atom, PrevChar} of
187 {<<>>, none} ->
188 {error, nil};
189 {<<First, _/binary>>, none} when ?is_digit_char(First) ->
190 {error, nil};
191 {<<"_", _/binary>>, none} ->
192 {error, nil};
193 {<<"_">>, _} ->
194 {error, nil};
195 {<<"_", _/binary>>, $_} ->
196 {error, nil};
197 {<<First, _/binary>>, _} when not ?is_atom_char(First) ->
198 {error, nil};
199 {<<First, Rest/binary>>, none} ->
200 inspect@maybe_gleam_atom(Rest, First, <<Acc/binary, (inspect@uppercase(First))>>);
201 {<<"_", Rest/binary>>, _} ->
202 inspect@maybe_gleam_atom(Rest, $_, Acc);
203 {<<First, Rest/binary>>, $_} ->
204 inspect@maybe_gleam_atom(Rest, First, <<Acc/binary, (inspect@uppercase(First))>>);
205 {<<First, Rest/binary>>, _} ->
206 inspect@maybe_gleam_atom(Rest, First, <<Acc/binary, First>>);
207 {<<>>, _} ->
208 {ok, Acc};
209 _ ->
210 erlang:throw({gleam_error, echo, Atom, PrevChar, Acc})
211 end.
212
213inspect@uppercase(X) -> X - 32.