158 lines
4.3 KiB
Erlang
158 lines
4.3 KiB
Erlang
-module(elixir).
|
|
-behaviour(application).
|
|
-export([main/1, start_cli/0,
|
|
scope_for_eval/1, scope_for_eval/2,
|
|
eval/2, eval/3, eval/4, file_type/1, file_type/2,
|
|
eval_quoted/2, eval_quoted/3, eval_quoted/4,
|
|
eval_forms/3, translate_forms/3]).
|
|
-include("elixir.hrl").
|
|
-include_lib("kernel/include/file.hrl").
|
|
|
|
%% Top level types
|
|
-export_type([char_list/0, as_boolean/1]).
|
|
-type char_list() :: string().
|
|
-type as_boolean(T) :: T.
|
|
|
|
% OTP APPLICATION API
|
|
|
|
-export([start/2, stop/1, config_change/3]).
|
|
|
|
start(_Type, _Args) ->
|
|
%% Set the shell to unicode so printing inside scripts work
|
|
%% Those can take a while, so let's do it in a new process
|
|
spawn(fun() ->
|
|
io:setopts(standard_io, [binary,{encoding,unicode}]),
|
|
io:setopts(standard_error, [binary,{encoding,unicode}])
|
|
end),
|
|
elixir_sup:start_link([]).
|
|
|
|
stop(_S) ->
|
|
ok.
|
|
|
|
config_change(_Changed, _New, _Remove) ->
|
|
ok.
|
|
|
|
%% escript entry point
|
|
|
|
main(Args) ->
|
|
application:start(?MODULE),
|
|
'Elixir.Kernel.CLI':main(Args).
|
|
|
|
%% Boot and process given options. Invoked by Elixir's script.
|
|
|
|
start_cli() ->
|
|
application:start(?MODULE),
|
|
'Elixir.Kernel.CLI':main(init:get_plain_arguments()).
|
|
|
|
%% Helpers
|
|
|
|
file_type(File) ->
|
|
file_type(File, read_link_info).
|
|
|
|
file_type(File, Op) ->
|
|
case file:Op(File) of
|
|
{ ok, #file_info{type=Type} } -> { ok, Type };
|
|
{ error, _ } = Error -> Error
|
|
end.
|
|
|
|
%% EVAL HOOKS
|
|
|
|
scope_for_eval(Opts) ->
|
|
scope_for_eval(#elixir_scope{
|
|
file = <<"nofile">>,
|
|
local = nil,
|
|
aliases = [],
|
|
requires = elixir_dispatch:default_requires(),
|
|
functions = elixir_dispatch:default_functions(),
|
|
macros = elixir_dispatch:default_macros()
|
|
}, Opts).
|
|
|
|
scope_for_eval(Scope, Opts) ->
|
|
File = case lists:keyfind(file, 1, Opts) of
|
|
{ file, RawFile } -> to_binary(RawFile);
|
|
false -> Scope#elixir_scope.file
|
|
end,
|
|
|
|
Local = case lists:keyfind(delegate_locals_to, 1, Opts) of
|
|
{ delegate_locals_to, LocalOpt } -> LocalOpt;
|
|
false -> Scope#elixir_scope.local
|
|
end,
|
|
|
|
Aliases = case lists:keyfind(aliases, 1, Opts) of
|
|
{ aliases, AliasesOpt } -> AliasesOpt;
|
|
false -> Scope#elixir_scope.aliases
|
|
end,
|
|
|
|
Requires = case lists:keyfind(requires, 1, Opts) of
|
|
{ requires, List } -> ordsets:from_list(List);
|
|
false -> Scope#elixir_scope.requires
|
|
end,
|
|
|
|
Functions = case lists:keyfind(functions, 1, Opts) of
|
|
{ functions, FunctionsOpt } -> FunctionsOpt;
|
|
false -> Scope#elixir_scope.functions
|
|
end,
|
|
|
|
Macros = case lists:keyfind(macros, 1, Opts) of
|
|
{ macros, MacrosOpt } -> MacrosOpt;
|
|
false -> Scope#elixir_scope.macros
|
|
end,
|
|
|
|
Scope#elixir_scope{
|
|
file=File, local=Local,
|
|
macros=Macros, functions=Functions,
|
|
requires=Requires, aliases=Aliases }.
|
|
|
|
%% String evaluation
|
|
|
|
eval(String, Binding) -> eval(String, Binding, []).
|
|
|
|
eval(String, Binding, Opts) ->
|
|
case lists:keyfind(line, 1, Opts) of
|
|
false -> Line = 1;
|
|
{ line, Line } -> []
|
|
end,
|
|
eval(String, Binding, Line, scope_for_eval(Opts)).
|
|
|
|
eval(String, Binding, Line, #elixir_scope{file=File} = S) when
|
|
is_list(String), is_list(Binding), is_integer(Line), is_binary(File) ->
|
|
Forms = elixir_translator:'forms!'(String, Line, File, []),
|
|
eval_forms(Forms, Binding, S).
|
|
|
|
%% Quoted evaluation
|
|
|
|
eval_quoted(Tree, Binding) -> eval_quoted(Tree, Binding, []).
|
|
|
|
eval_quoted(Tree, Binding, Opts) ->
|
|
case lists:keyfind(line, 1, Opts) of
|
|
{ line, Line } -> [];
|
|
false -> Line = 1
|
|
end,
|
|
eval_quoted(Tree, Binding, Line, scope_for_eval(Opts)).
|
|
|
|
eval_quoted(Tree, Binding, Line, #elixir_scope{} = S) when is_integer(Line) ->
|
|
eval_forms(elixir_quote:linify(Line, Tree), Binding, S).
|
|
|
|
%% Handle forms evaluation internally, it is an
|
|
%% internal API not meant for external usage.
|
|
|
|
translate_forms(Tree, Binding, Opts) when is_list(Opts) ->
|
|
translate_forms(Tree, Binding, scope_for_eval(Opts));
|
|
|
|
translate_forms(Tree, Binding, #elixir_scope{} = Scope) ->
|
|
elixir_translator:translate(Tree, elixir_scope:vars_from_binding(Scope, Binding)).
|
|
|
|
eval_forms(Tree, Binding, Scope) ->
|
|
{ ParseTree, NewScope } = translate_forms(Tree, Binding, Scope),
|
|
case ParseTree of
|
|
[] -> { nil, Binding, NewScope };
|
|
_ ->
|
|
{value, Value, NewBinding} = erl_eval:exprs(ParseTree, elixir_scope:binding_for_eval(Binding, nil)),
|
|
{Value, elixir_scope:binding_from_vars(NewScope, NewBinding), NewScope }
|
|
end.
|
|
|
|
%% INTERNAL HELPERS
|
|
|
|
to_binary(Bin) when is_binary(Bin) -> Bin;
|
|
to_binary(List) when is_list(List) -> list_to_binary(List).
|