186 lines
5.1 KiB
Elixir
186 lines
5.1 KiB
Elixir
defmodule Code do
|
|
@moduledoc """
|
|
The Code module is responsible to manage code compilation,
|
|
code evaluation and code loading.
|
|
"""
|
|
|
|
@doc """
|
|
Returns all the loaded files.
|
|
"""
|
|
def loaded_files do
|
|
server_call :loaded
|
|
end
|
|
|
|
@doc """
|
|
Appends a path to Erlang VM code path.
|
|
"""
|
|
def append_path(path) do
|
|
Erlang.code.add_pathz(to_char_list(path))
|
|
end
|
|
|
|
@doc """
|
|
Prepends a path to Erlang VM code path.
|
|
"""
|
|
def prepend_path(path) do
|
|
Erlang.code.add_patha(to_char_list(path))
|
|
end
|
|
|
|
@doc """
|
|
Evalutes the contents given by string. The second argument is the binding
|
|
(which should be a Keyword) followed by a keyword list of options. The
|
|
options can be:
|
|
|
|
* `:file` - the file to be considered in the evaluation
|
|
* `:line` - the line the script starts
|
|
* `:delegate_locals_to` - delegate local calls to the given module,
|
|
otherwise functions are evaluated inside Erlang's default scope.
|
|
|
|
## Examples
|
|
|
|
Code.eval "a + b", [a: 1, b: 2], file: __FILE__, line: __LINE__
|
|
#=> { 3, [ {:a, 1}, {:b, 2} ] }
|
|
|
|
"""
|
|
def eval(string, binding // [], opts // []) do
|
|
{ value, binding, _scope } =
|
|
Erlang.elixir.eval :unicode.characters_to_list(string), binding, opts
|
|
{ value, binding }
|
|
end
|
|
|
|
@doc """
|
|
Evalutes the quoted contents.
|
|
|
|
## Options
|
|
|
|
This function accepts a list of options. The supported
|
|
options are:
|
|
|
|
* `:file` - The filename to be used in stacktraces
|
|
or by the __FILE__ macro in case there is an error.
|
|
|
|
* `:line` - The line to be used when expanding __LINE__
|
|
macros and expressions inside the quote.
|
|
|
|
## Examples
|
|
|
|
contents = quote hygiene: false, do: a + b
|
|
Code.eval_quoted contents, [a: 1, b: 2], file: __FILE__, line: __LINE__
|
|
#=> { 3, [ {:a, 1}, {:b, 2} ] }
|
|
|
|
"""
|
|
def eval_quoted(quoted, binding // [], opts // []) do
|
|
{ value, binding, _scope } =
|
|
Erlang.elixir.eval_quoted [quoted], binding, opts
|
|
{ value, binding }
|
|
end
|
|
|
|
@doc """
|
|
Loads the given `file`. Accepts `relative_to` as an argument to tell
|
|
where the file is located. If the file was already required/loaded,
|
|
loads it again. It returns the full path of the loaded file.
|
|
|
|
When loading a file, you may skip passing .exs as extension as Elixir
|
|
automatically adds it for you.
|
|
"""
|
|
def load_file(file, relative_to // nil) do
|
|
file = find_file(file, relative_to)
|
|
server_call { :loaded, file }
|
|
Erlang.elixir_compiler.file to_char_list(file)
|
|
file
|
|
end
|
|
|
|
@doc """
|
|
Requires the given `file`. Accepts `relative_to` as an argument to tell
|
|
where the file is located. If the file was already required/loaded,
|
|
returns nil, otherwise the full path of the loaded file.
|
|
|
|
When requiring a file, you may skip passing .exs as extension as
|
|
Elixir automatically adds it for you.
|
|
"""
|
|
def require_file(file, relative_to // nil) do
|
|
file = find_file(file, relative_to)
|
|
|
|
case server_call({ :loaded, file }) do
|
|
:ok ->
|
|
Erlang.elixir_compiler.file to_char_list(file)
|
|
file
|
|
:duplicated -> nil
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Loads the compilation options from the code server.
|
|
Check compiler_options/1 for more information.
|
|
"""
|
|
def compiler_options do
|
|
server_call :compiler_options
|
|
end
|
|
|
|
@doc """
|
|
Sets compilation options. Those options are global
|
|
since they are stored by Elixir's Code Server.
|
|
|
|
Available options are:
|
|
|
|
* docs - when true, retain documentation in the compiled module;
|
|
* debug_info - when true, retain debug information in the compiled module.
|
|
Notice debug information can be used to reconstruct the source code;
|
|
* ignore_module_conflict - when true, override modules that were already defined;
|
|
|
|
"""
|
|
def compiler_options(opts) do
|
|
server_call { :compiler_options, opts }
|
|
end
|
|
|
|
@doc """
|
|
Compiles the given string and returns a list of tuples where
|
|
the first element is the module name and the second one is its
|
|
binary.
|
|
|
|
For compiling many files at once, check `Elixir.ParallelCompiler`.
|
|
"""
|
|
def compile_string(string, file // 'nofile') do
|
|
Erlang.elixir_compiler.string :unicode.characters_to_list(string), to_char_list(file)
|
|
end
|
|
|
|
@doc """
|
|
Ensure if the given module is loaded. If the module is already loaded,
|
|
it works as no-op. If the module was not loaded yet, it tries to load it.
|
|
|
|
If it succeeds loading the module anyhow, it returns `{ :module, module }`.
|
|
If not, returns `{ :error, reason }` with the error reason.
|
|
"""
|
|
def ensure_loaded(module) when is_atom(module) do
|
|
Erlang.code.ensure_loaded(module)
|
|
end
|
|
|
|
## Helpers
|
|
|
|
# Finds the file given the relative_to path.
|
|
# If the file is found, returns its path in binary, fails otherwise.
|
|
defp find_file(file, relative_to) do
|
|
file = to_binary(file)
|
|
|
|
file = if relative_to do
|
|
File.expand_path(file, relative_to)
|
|
else
|
|
File.expand_path(file)
|
|
end
|
|
|
|
if File.regular?(file) do
|
|
file
|
|
else
|
|
prefix = "#{file}.exs"
|
|
if File.regular?(prefix) do
|
|
prefix
|
|
else
|
|
raise ArgumentError, message: "could not load #{file}"
|
|
end
|
|
end
|
|
end
|
|
|
|
defp server_call(args) do
|
|
Erlang.gen_server.call(:elixir_code_server, args)
|
|
end
|
|
end
|