Fork of daniellemaywood.uk/gleam — Wasm codegen work
3.9 kB
118 lines
1-module('{{ application }}@@main').
2-export([run/1, main/1]).
3
4-define(red, "\e[31;1m").
5-define(grey, "\e[90m").
6-define(reset_color, "\e[39m").
7-define(reset_all, "\e[0m").
8
9%%% Used by `gleam run`
10run(Module) ->
11 io:setopts(standard_io, [binary, {encoding, utf8}]),
12 io:setopts(standard_error, [{encoding, utf8}]),
13 process_flag(trap_exit, true),
14 Pid = spawn_link(fun() -> run_module(Module) end),
15 receive
16 {'EXIT', Pid, {Reason, [First|_] = StackTrace}} when is_tuple(First) ->
17 print_error_with_stacktrace(exit, Reason, StackTrace),
18 init:stop(1);
19 {'EXIT', Pid, Reason} when Reason =/= normal ->
20 print_error(exit, Reason),
21 init:stop(1)
22 end.
23
24%%% Used by escripts
25main(_) ->
26 run('{{ application }}').
27
28run_module(Module) ->
29 try
30 {ok, _} = application:ensure_all_started('{{ application }}'),
31 erlang:process_flag(trap_exit, false),
32 Module:main(),
33 init:stop(0)
34 catch
35 Class:Reason:StackTrace ->
36 print_error_with_stacktrace(Class, Reason, StackTrace),
37 init:stop(1)
38 end.
39
40print_error_with_stacktrace(Class, Error, Stacktrace) ->
41 Printed = [
42 ?red, "runtime error", ?reset_color, ": ", error_class(Class, Error), ?reset_all,
43 "\n\n",
44 error_message(Error),
45 "\n\n",
46 error_details(Class, Error),
47 "stacktrace:\n",
48 [error_frame(Line) || Line <- refine_first(Error, Stacktrace)]
49 ],
50 io:format(standard_error, "~ts~n", [Printed]).
51
52print_error(Class, Error) ->
53 Printed = [
54 ?red, "runtime error", ?reset_color, ": ", error_class(Class, Error), ?reset_all,
55 "\n\n",
56 error_message(Error),
57 "\n\n",
58 "exit reason:\n ", print_term(Error), $\n
59 ],
60 io:format(standard_error, "~ts~n", [Printed]).
61
62refine_first(#{gleam_error := _, line := L}, [{M, F, A, [{file, Fi} | _]} | S]) ->
63 [{M, F, A, [{file, Fi}, {line, L}]} | S];
64refine_first(_, S) ->
65 S.
66
67error_class(_, #{gleam_error := panic}) -> "panic";
68error_class(_, #{gleam_error := todo}) -> "todo";
69error_class(_, #{gleam_error := let_assert}) -> "let assert";
70error_class(_, #{gleam_error := assert}) -> "assert";
71error_class(Class, _) -> ["Erlang ", atom_to_binary(Class)].
72
73error_message(#{gleam_error := _, message := M}) ->
74 M;
75error_message(undef) ->
76 <<"A function was called but it did not exist."/utf8 >>;
77error_message({case_clause, _}) ->
78 <<"No pattern matched in an Erlang case expression."/utf8>>;
79error_message({badmatch, _}) ->
80 <<"An Erlang assignment pattern did not match."/utf8>>;
81error_message(function_clause) ->
82 <<"No Erlang function clause matched the arguments it was called with."/utf8>>;
83error_message(_) ->
84 <<"An error occurred outside of Gleam."/utf8>>.
85
86error_details(_, #{gleam_error := let_assert, value := V}) ->
87 ["unmatched value:\n ", print_term(V), $\n, $\n];
88error_details(_, {case_clause, V}) ->
89 ["unmatched value:\n ", print_term(V), $\n, $\n];
90error_details(_, {badmatch, V}) ->
91 ["unmatched value:\n ", print_term(V), $\n, $\n];
92error_details(_, #{gleam_error := _}) ->
93 [];
94error_details(error, function_clause) ->
95 [];
96error_details(error, undef) ->
97 [];
98error_details(C, E) ->
99 ["erlang:", atom_to_binary(C), $(, print_term(E), $), $\n, $\n].
100
101print_term(T) ->
102 try
103 gleam@string:inspect(T)
104 catch
105 _:_ -> io_lib:format("~p", [T])
106 end.
107
108error_frame({?MODULE, _, _, _}) -> [];
109error_frame({erl_eval, _, _, _}) -> [];
110error_frame({init, _, _, _}) -> [];
111error_frame({M, F, _, O}) ->
112 M1 = string:replace(atom_to_binary(M), "@", "/", all),
113 [" ", M1, $., atom_to_binary(F), error_frame_end(O), $\n].
114
115error_frame_end([{file, Fi}, {line, L} | _]) ->
116 [?grey, $\s, Fi, $:, integer_to_binary(L), ?reset_all];
117error_frame_end(_) ->
118 [?grey, " unknown source", ?reset_all].