Current section

Files

Jump to
sourceror src sourceror_errors.erl
Raw

src/sourceror_errors.erl

-module(sourceror_errors).
-export([parse_error/4]).
-include("sourceror_elixir.hrl").
%% Tokenization parsing/errors.
-spec parse_error(elixir:keyword(), binary() | {binary(), binary()},
binary(), binary()) -> no_return().
parse_error(Location, File, Error, <<>>) ->
Message = case Error of
<<"syntax error before: ">> -> <<"syntax error: expression is incomplete">>;
_ -> Error
end,
raise(Location, File, 'Elixir.TokenMissingError', Message);
%% Show a nicer message for end of line
parse_error(Location, File, <<"syntax error before: ">>, <<"eol">>) ->
raise(Location, File, 'Elixir.SyntaxError',
<<"unexpectedly reached end of line. The current expression is invalid or incomplete">>);
%% Show a nicer message for keywords pt1 (Erlang keywords show up wrapped in single quotes)
parse_error(Location, File, <<"syntax error before: ">>, Keyword)
when Keyword == <<"'not'">>;
Keyword == <<"'and'">>;
Keyword == <<"'or'">>;
Keyword == <<"'when'">>;
Keyword == <<"'after'">>;
Keyword == <<"'catch'">>;
Keyword == <<"'end'">> ->
raise_reserved(Location, File, binary_part(Keyword, 1, byte_size(Keyword) - 2));
%% Show a nicer message for keywords pt2 (Elixir keywords show up as is)
parse_error(Location, File, <<"syntax error before: ">>, Keyword)
when Keyword == <<"fn">>;
Keyword == <<"else">>;
Keyword == <<"rescue">>;
Keyword == <<"true">>;
Keyword == <<"false">>;
Keyword == <<"nil">>;
Keyword == <<"in">> ->
raise_reserved(Location, File, Keyword);
%% Produce a human-readable message for errors before a sigil
parse_error(Location, File, <<"syntax error before: ">>, <<"{sigil,", _Rest/binary>> = Full) ->
{sigil, _, Sigil, [Content | _], _, _, _} = parse_erl_term(Full),
Content2 = case is_binary(Content) of
true -> Content;
false -> <<>>
end,
Message = <<"syntax error before: sigil \~", Sigil, " starting with content '", Content2/binary, "'">>,
raise(Location, File, 'Elixir.SyntaxError', Message);
%% Binaries (and interpolation) are wrapped in [<<...>>]
parse_error(Location, File, Error, <<"[", _/binary>> = Full) when is_binary(Error) ->
Term = case parse_erl_term(Full) of
[H | _] when is_binary(H) -> <<$", H/binary, $">>;
_ -> <<$">>
end,
raise(Location, File, 'Elixir.SyntaxError', <<Error/binary, Term/binary>>);
%% Given a string prefix and suffix to insert the token inside the error message rather than append it
parse_error(Location, File, {ErrorPrefix, ErrorSuffix}, Token) when is_binary(ErrorPrefix), is_binary(ErrorSuffix), is_binary(Token) ->
Message = <<ErrorPrefix/binary, Token/binary, ErrorSuffix/binary >>,
raise(Location, File, 'Elixir.SyntaxError', Message);
%% Misplaced char tokens (for example, {char, _, 97}) are translated by Erlang into
%% the char literal (i.e., the token in the previous example becomes $a),
%% because {char, _, _} is a valid Erlang token for an Erlang char literal. We
%% want to represent that token as ?a in the error, according to the Elixir
%% syntax.
parse_error(Location, File, <<"syntax error before: ">>, <<$$, Char/binary>>) ->
Message = <<"syntax error before: ?", Char/binary>>,
raise(Location, File, 'Elixir.SyntaxError', Message);
%% Everything else is fine as is
parse_error(Location, File, Error, Token) when is_binary(Error), is_binary(Token) ->
Message = <<Error/binary, Token/binary >>,
raise(Location, File, 'Elixir.SyntaxError', Message).
parse_erl_term(Term) ->
{ok, Tokens, _} = erl_scan:string(binary_to_list(Term)),
{ok, Parsed} = erl_parse:parse_term(Tokens ++ [{dot, 1}]),
Parsed.
raise_reserved(Location, File, Keyword) ->
raise(Location, File, 'Elixir.SyntaxError',
<<"syntax error before: ", Keyword/binary, ". \"", Keyword/binary, "\" is a "
"reserved word in Elixir and therefore its usage is limited. For instance, "
"it can't be used as a variable or be defined nor invoked as a regular function">>).
raise(Location, File, Kind, Message) when is_binary(File) ->
raise(Kind, Message, [{file, File} | Location]).
raise(Kind, Message, Opts) when is_binary(Message) ->
Stacktrace = try throw(ok) catch _:_:Stack -> Stack end,
Exception = Kind:exception([{description, Message} | Opts]),
erlang:raise(error, Exception, tl(Stacktrace)).