Packages
sourceror
0.8.5
1.12.2
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.2
1.8.0
1.7.1
1.7.0
1.6.0
1.5.0
1.4.0
1.3.0
1.2.1
1.2.0
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.14.1
0.14.0
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.2
Utilities to work with Elixir source code.
Current section
Files
Jump to
Current section
Files
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)).