diff options
author | Carlos Galdino <carloshsgaldino@gmail.com> | 2012-02-29 17:34:38 -0300 |
---|---|---|
committer | Carlos Galdino <carloshsgaldino@gmail.com> | 2012-02-29 17:34:38 -0300 |
commit | affe89a4d6e2ca052930399ce429e9c62b40f1d5 (patch) | |
tree | b4ba8b1d5611a165f21103ea1912618bf9681a9a /tests | |
parent | 408df12105f7a972b94e240355f4b66540720704 (diff) | |
download | pygments-affe89a4d6e2ca052930399ce429e9c62b40f1d5.tar.gz |
Add Elixir example file
Diffstat (limited to 'tests')
-rw-r--r-- | tests/examplefiles/example_elixir.ex | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/tests/examplefiles/example_elixir.ex b/tests/examplefiles/example_elixir.ex new file mode 100644 index 00000000..2e92163d --- /dev/null +++ b/tests/examplefiles/example_elixir.ex @@ -0,0 +1,363 @@ +# We cannot use to_char_list because it depends on inspect, +# which depends on protocol, which depends on this module. +import Elixir::Builtin, except: [to_char_list: 1] + +defmodule Module do + require Erlang.ets, as: ETS + + @moduledoc """ + This module provides many functions to deal with modules during + compilation time. It allows a developer to dynamically attach + documentation, merge data, register attributes and so forth. + + After the module is compiled, using many of the functions in + this module will raise errors, since it is out of their purpose + to inspect runtime data. Most of the runtime data can be inspected + via the `__info__(attr)` function attached to each compiled module. + """ + + @doc """ + Evalutes the quotes contents in the given module context. + Raises an error if the module was already compiled. + + ## Examples + + defmodule Foo do + contents = quote do: (def sum(a, b), do: a + b) + Module.eval_quoted __MODULE__, contents, [], __FILE__, __LINE__ + end + + Foo.sum(1, 2) #=> 3 + """ + def eval_quoted(module, quoted, binding, filename, line) do + assert_not_compiled!(:eval_quoted, module) + { binding, scope } = Erlang.elixir_module.binding_and_scope_for_eval(line, to_char_list(filename), module, binding) + Erlang.elixir_def.reset_last(module) + Erlang.elixir.eval_quoted([quoted], binding, line, scope) + end + + @doc """ + Checks if the module is compiled or not. + + ## Examples + + defmodule Foo do + Module.compiled?(__MODULE__) #=> false + end + + Module.compiled?(Foo) #=> true + + """ + def compiled?(module) do + table = data_table_for(module) + table == ETS.info(table, :name) + end + + @doc """ + Reads the data for the given module. This is used + to read data of uncompiled modules. If the module + was already compiled, you shoul access the data + directly by invoking `__info__(:data)` in that module. + + ## Examples + + defmodule Foo do + Module.merge_data __MODULE__, value: 1 + Module.read_data __MODULE__ #=> [value: 1] + end + + """ + def read_data(module) do + assert_not_compiled!(:read_data, module) + ETS.lookup_element(data_table_for(module), :data, 2) + end + + @doc """ + Reads the data from `module` at the given key `at`. + + ## Examples + + defmodule Foo do + Module.merge_data __MODULE__, value: 1 + Module.read_data __MODULE__, :value #=> 1 + end + + """ + def read_data(module, at) do + Orddict.get read_data(module), at + end + + @doc """ + Merge the given data into the module, overriding any + previous one. + + If any of the given data is a registered attribute, it is + automatically added to the attribute set, instead of marking + it as data. See register_attribute/2 and add_attribute/3 for + more info. + + ## Examples + + defmodule Foo do + Module.merge_data __MODULE__, value: 1 + end + + Foo.__info__(:data) #=> [value: 1] + + """ + def merge_data(module, data) do + assert_not_compiled!(:merge_data, module) + + table = data_table_for(module) + old = ETS.lookup_element(table, :data, 2) + registered = ETS.lookup_element(table, :registered_attributes, 2) + + { attrs, new } = Enum.partition data, fn({k,_}) -> List.member?(registered, k) end + Enum.each attrs, fn({k,v}) -> add_attribute(module, k, v) end + ETS.insert(table, { :data, Orddict.merge(old, new) }) + end + + @doc """ + Attaches documentation to a given function. It expects + the module the function belongs to, the line (a non negative + integer), the kind (def or defmacro), a tuple representing + the function and its arity and the documentation, which should + be either a binary or a boolean. + + ## Examples + + defmodule MyModule do + Module.add_doc(__MODULE__, __LINE__ + 1, :def, { :version, 0 }, "Manually added docs") + def version, do: 1 + end + + """ + def add_doc(module, line, kind, tuple, doc) when + is_binary(doc) or is_boolean(doc) do + assert_not_compiled!(:add_doc, module) + case kind do + match: :defp + :warn + else: + table = docs_table_for(module) + ETS.insert(table, { tuple, line, kind, doc }) + :ok + end + end + + @doc """ + Checks if a function was defined, regardless if it is + a macro or a private function. Use function_defined?/3 + to assert for an specific type. + + ## Examples + + defmodule Example do + Module.function_defined? __MODULE__, { :version, 0 } #=> false + def version, do: 1 + Module.function_defined? __MODULE__, { :version, 0 } #=> true + end + + """ + def function_defined?(module, tuple) when is_tuple(tuple) do + assert_not_compiled!(:function_defined?, module) + table = function_table_for(module) + ETS.lookup(table, tuple) != [] + end + + @doc """ + Checks if a function was defined and also for its `kind`. + `kind` can be either :def, :defp or :defmacro. + + ## Examples + + defmodule Example do + Module.function_defined? __MODULE__, { :version, 0 }, :defp #=> false + def version, do: 1 + Module.function_defined? __MODULE__, { :version, 0 }, :defp #=> false + end + + """ + def function_defined?(module, tuple, kind) do + List.member? defined_functions(module, kind), tuple + end + + @doc """ + Return all functions defined in the given module. + + ## Examples + + defmodule Example do + def version, do: 1 + Module.defined_functions __MODULE__ #=> [{:version,1}] + end + + """ + def defined_functions(module) do + assert_not_compiled!(:defined_functions, module) + table = function_table_for(module) + lc { tuple, _, _ } in ETS.tab2list(table), do: tuple + end + + @doc """ + Returns all functions defined in te given module according + to its kind. + + ## Examples + + defmodule Example do + def version, do: 1 + Module.defined_functions __MODULE__, :def #=> [{:version,1}] + Module.defined_functions __MODULE__, :defp #=> [] + end + + """ + def defined_functions(module, kind) do + assert_not_compiled!(:defined_functions, module) + table = function_table_for(module) + entry = kind_to_entry(kind) + ETS.lookup_element(table, entry, 2) + end + + @doc """ + Adds a compilation callback hook that is invoked + exactly before the module is compiled. + + This callback is useful when used with `use` as a mechanism + to clean up any internal data in the module before it is compiled. + + ## Examples + + Imagine you are creating a module/library that is meant for + external usage called `MyLib`. It could be defined as: + + defmodule MyLib do + def __using__(target) do + Module.merge_data target, some_data: true + Module.add_compile_callback(target, __MODULE__, :__callback__) + end + + defmacro __callback__(target) do + value = Orddict.get(Module.read_data(target), :some_data, []) + quote do: (def my_lib_value, do: unquote(value)) + end + end + + And a module could use `MyLib` with: + + defmodule App do + use ModuleTest::ToBeUsed + end + + In the example above, `MyLib` defines a data to the target. This data + can be updated throughout the module definition and therefore, the final + value of the data can only be compiled using a compiation callback, + which will read the final value of :some_data and compile to a function. + """ + def add_compile_callback(module, target, fun // :__compiling__) do + assert_not_compiled!(:add_compile_callback, module) + new = { target, fun } + table = data_table_for(module) + old = ETS.lookup_element(table, :compile_callbacks, 2) + ETS.insert(table, { :compile_callbacks, [new|old] }) + end + + @doc """ + Adds an Erlang attribute to the given module with the given + key and value. The same attribute can be added more than once. + + ## Examples + + defmodule MyModule do + Module.add_attribute __MODULE__, :custom_threshold_for_lib, 10 + end + + """ + def add_attribute(module, key, value) when is_atom(key) do + assert_not_compiled!(:add_attribute, module) + table = data_table_for(module) + attrs = ETS.lookup_element(table, :attributes, 2) + ETS.insert(table, { :attributes, [{key, value}|attrs] }) + end + + @doc """ + Deletes all attributes that matches the given key. + + ## Examples + + defmodule MyModule do + Module.add_attribute __MODULE__, :custom_threshold_for_lib, 10 + Module.delete_attribute __MODULE__, :custom_threshold_for_lib + end + + """ + def delete_attribute(module, key) when is_atom(key) do + assert_not_compiled!(:delete_attribute, module) + table = data_table_for(module) + attrs = ETS.lookup_element(table, :attributes, 2) + final = lc {k,v} in attrs, k != key, do: {k,v} + ETS.insert(table, { :attributes, final }) + end + + @doc """ + Registers an attribute. This allows a developer to use the data API + but Elixir will register the data as an attribute automatically. + By default, `vsn`, `behavior` and other Erlang attributes are + automatically registered. + + ## Examples + + defmodule MyModule do + Module.register_attribute __MODULE__, :custom_threshold_for_lib + @custom_threshold_for_lib 10 + end + + """ + def register_attribute(module, new) do + assert_not_compiled!(:register_attribute, module) + table = data_table_for(module) + old = ETS.lookup_element(table, :registered_attributes, 2) + ETS.insert(table, { :registered_attributes, [new|old] }) + end + + @doc false + # Used internally to compile documentation. This function + # is private and must be used only internally. + def compile_doc(module, line, kind, pair) do + case read_data(module, :doc) do + match: nil + # We simply discard nil + match: doc + result = add_doc(module, line, kind, pair, doc) + merge_data(module, doc: nil) + result + end + end + + ## Helpers + + defp kind_to_entry(:def), do: :public + defp kind_to_entry(:defp), do: :private + defp kind_to_entry(:defmacro), do: :macros + + defp to_char_list(list) when is_list(list), do: list + defp to_char_list(bin) when is_binary(bin), do: binary_to_list(bin) + + defp data_table_for(module) do + list_to_atom Erlang.lists.concat([:d, module]) + end + + defp function_table_for(module) do + list_to_atom Erlang.lists.concat([:f, module]) + end + + defp docs_table_for(module) do + list_to_atom Erlang.lists.concat([:o, module]) + end + + defp assert_not_compiled!(fun, module) do + compiled?(module) || + raise ArgumentError, message: + "could not call #{fun} on module #{module} because it was already compiled" + end +end
\ No newline at end of file |