Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 0c98716ef8
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 100 lines (74 sloc) 3.376 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
% Holds the logic responsible for defining overridable functions and handling super.
-module(elixir_def_overridable).
-export([define/3, store_pending/1, is_defined/2, ensure_defined/4,
  assign_args/3, retrieve_args/3, name/2, store/3, format_error/1]).
-include("elixir.hrl").

overridable(Module) ->
  ets:lookup_element(elixir_module:data_table(Module), overridable, 2).

overridable(Module, Value) ->
  ets:insert(elixir_module:data_table(Module), { overridable, Value }).

%% Add new function to the overridable dictionary.

define(Module, Tuple, Args) ->
  Old = overridable(Module),
  New = [{ Tuple, { 1, [Args] } }],
  Abstract = orddict:merge(fun(_K, { Count, V1 }, _V2) -> { Count + 1, [Args|V1] } end, Old, New),
  overridable(Module, Abstract).

%% Check if an overridable function is defined.

is_defined(Module, Tuple) ->
  Overridable = overridable(Module),
  case orddict:find(Tuple, Overridable) of
    { ok, { _, [_|_] } } -> true;
    _ -> false
  end.

ensure_defined(Line, Module, Tuple, S) ->
  case elixir_def_overridable:is_defined(Module, Tuple) of
    true -> [];
    _ -> elixir_errors:form_error(Line, S#elixir_scope.filename, ?MODULE, { no_super, Module, Tuple })
  end.

%% Retrieve args defined for the given arity.

retrieve_args(Line, Arity, S) ->
  {
    [ { var, Line, super_arg(X) } || X <- lists:seq(1, Arity) ],
    S#elixir_scope{name_args=true}
  }.

%% Assign pseudo variables to the given vars.

assign_args(Line, Args, S) ->
  { FArgs, _ } = lists:mapfoldl(fun(X, Acc) -> assign_args(Line, X, Acc, S) end, 1, Args),
  FArgs.

assign_args(Line, X, Acc, _) ->
  Match = { match, Line, X, { var, Line, super_arg(Acc) } },
  { Match, Acc + 1 }.

super_arg(Counter) ->
  ?ELIXIR_ATOM_CONCAT(['_EXS', Counter]).

%% Gets the name based on the function and stored overridables

name(Module, Function) ->
  name(Module, Function, overridable(Module)).

name(_Module, { Name, _ } = Function, Overridable) ->
  { Count, _ } = orddict:fetch(Function, Overridable),
  ?ELIXIR_ATOM_CONCAT(["OVERRIDABLE-", Count, "-", Name]).

%% Store

store(Module, Function, GenerateName) ->
  Overridable = overridable(Module),
  { Count, [H|T] } = orddict:fetch(Function, Overridable),
  overridable(Module, orddict:store(Function, { Count, T }, Overridable)),

  { { Name, Arity }, Line, Filename, Kind, Defaults, Clauses } = H,

  { FinalKind, FinalName } = case GenerateName of
    true -> { defp, name(Module, Function, Overridable) };
    false -> { Kind, Name }
  end,

  Def = { function, Line, FinalName, Arity, Clauses },
  elixir_def:store_each(false, FinalKind, Filename, elixir_def:table(Module), Defaults, Def).

%% Store pending declarations that were not manually made concrete.

store_pending(Module) ->
  [store(Module, X, false) || { X, { _, [_|_] } } <- overridable(Module),
    not '__MAIN__.Module':'function_defined?'(Module, X)].

%% Error handling

format_error({ no_super, Module, { Name, Arity } }) ->
  Bins = [ format_fa(X) || { X, { _, [_|_] } } <- overridable(Module)],
  Joined = '__MAIN__.Enum':join(Bins, <<", ">>),
  io_lib:format("no super defined for ~s/~B in module ~p. Overridable functions available are: ~s",
    [Name, Arity, elixir_errors:inspect(Module), Joined]).

format_fa({ Name, Arity }) ->
  A = atom_to_binary(Name, utf8),
  B = list_to_binary(integer_to_list(Arity)),
  << A/binary, $/, B/binary >>.
Something went wrong with that request. Please try again.