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