smerl (util v1.3.5)

View Source

Simple Metaprogramming for Erlang

Smerl is an Erlang library that simplifies the creation and manipulation of Erlang modules in runtime.

You don't need to know Smerl in order to use ErlyWeb; Smerl is included in ErlyWeb because ErlyWeb uses it internally.

Smerl uses Erlang's capabilities for hot code swapping and abstract syntax tree transformations to do its magic. Smerl is inspired by the rdbms_codegen.erl module in the RDBMS application written by Ulf Wiger. RDBMS is part of Jungerl ([http://jungerl.sf.net]).

Here's a quick example illustrating how to use Smerl:

test_smerl() ->
  M1 = smerl:new(foo),
  {ok, M2} = smerl:add_func(M1, "bar() -> 1 + 1."),
  smerl:compile(M2),
  foo:bar(),   % returns 2
  smerl:has_func(M2, bar, 0). % returns true

New functions can be expressed either as strings of Erlang code or as abstract forms. For more information, read the Abstract Format section in the ERTS User's guide ([http://erlang.org/doc/doc-5.5/erts-5.5/doc/html/absform.html#4]).

Using the abstract format, the 3rd line of the above example would be written as

   {ok,M2} = smerl:add_func(M1, {function,1,bar,0,
                            [{clause,1,[],[],
                             [{op,1,'+',{integer,1,1},{integer,1,1}}]}]).

<p>The abstact format may look more verbose in this example, but it's also easier to manipulate in code.</p> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==================================================================== [ EOH ]

Author: Yariv Sadan Copyright: 2006-2007, 2016 AUTHORS

Summary

Types

A list of export()s.

The abstract form for the function, as described in the ERTS Users' manual.

A list of func_form()s.

A data structure holding the abstract representation for a module.

Functions

Add a new exported function to MetaMod.

Add Function to MetaMod and return the new meta_mod(). If Export is true, add Function to MetaMod's exports.

Compile MetaMod and load the resulting BEAM into the emulator.

Compile MetaMod and load the resulting BEAM into the emulator. Options is a list of options as described in the compile module in the Erlang documentation. If an outdir is provided, write the .beam file to it.

Get the curried form for Form with Args. Here, "currying" involves replacing one or more of the function's leading arguments with predefined values.

Curry Module:Function/Arity with the given Args.

Curry Module:Function/Aritywith the given Args, renaming it to NewName and return the renamed form.

Add Form curried with Args to MetaMod.

Add Function/Arity curried with Args to MetaMod.

Curry MetaMod:Function/Arity and add it to MetaMod as NewName.

Curry Module:Function/Arity and add it to MetaMod as NewName.

Replace the function represented by Form in MetaMod with its curried form.

Replace Function/Arity in MetaMod with its curried form.

Apply {@link embed_args/2} with Values to all forms in MetaMod. exports for functions whose arities change are preserved.

Replace the arguments of the function represented by Form, where the argument's Name matches an element from Vals with the corresponding Value.

Apply {@link embed_args/2} to MetaMod:Function/Arity and add the resulting function to MetMod, after renaming it to NewName. See: rename/2.

Add aliases for Parent's functions missing from Child to Child. The new functions in Child are shallow, i.e. they have the name and arity of the corresponding functions in Parent, but instead of implementing their logic they call the Parent functions.

Similar to {@link extend/2}, with the addition of ArityDiff, which indicates the difference in arities <em>Smerl</em> should use when figuring out which functions to generate based on the modules' exports. This is sometimes useful when calling {@link extend/3} followed by {@link embed_all/2}.

Create a meta_mod for a module from its source file.

Create a meta_mod tuple for an existing module. If ModuleName is a string, it is interpreted as a file name (this is the same as calling {@link for_file/3}). If ModuleName is an atom, <em>Smerl</em> attempts to find its abstract represtation either from its source file or from its .beam file directly (if it has been compiled with debug_info). If the abstract representation can't be found, this function returns an error.

Get the value of MetaMod's Key attribute.

Get the export_all value for MetaMod.

Return the list of exports in the meta_mod.

Return the list of function forms in the meta_mod.

Attempt to get the func_form() for MetaMod:Function/Arity.

Return the module name for the meta_mod.

Check whether MetaMod has a function Function/Arity.

Create a new meta_mod for a module with the given name.

Remove an export {Function, Arity} from the list of exports in MetaMod.

Try to remove Function from MetaMod. If the function exists, return the new meta_mod(). Otherwise, return MetaMod.

Change the name of the function represented by Form to NewName.

Replace an existing function with a new one. If a matching function doesn't exist, add Function to MetaMod. This is tantamount to calling {@link remove_func/3} followed by {@link add_func/2}.

Set the export_all value for MetaMod.

Set the MetaMod's export list to Exports.

Set the meta_mod's module name.

Return the pretty-printed source code for MetaMod.

Types

args()

-type args() :: term() | [term()].

error_t(Error)

-type error_t(Error) :: ok | {error, Error}.

export()

-type export() :: {Function :: atom(), Arity :: arity()}.

exports()

-type exports() :: [export()].

A list of export()s.

func_form()

-type func_form() :: erl_parse:abstract_form().

The abstract form for the function, as described in the ERTS Users' manual.

func_forms()

-type func_forms() :: [func_form()].

A list of func_form()s.

meta_mod()

-type meta_mod() ::
          #meta_mod{module :: module(),
                    file :: undefined | file:filename(),
                    exports :: exports(),
                    forms :: func_forms(),
                    export_all :: boolean()}.

A data structure holding the abstract representation for a module.

ok_t(Value)

-type ok_t(Value) :: {ok, Value} | error.

result(Value)

-type result(Value) :: result(Value, term()).

result(Value, Error)

-type result(Value, Error) :: {ok, Value} | {error, Error}.

Functions

add_func(MetaMod, Form)

-spec add_func(MetaMod, Form) -> result(meta_mod(), parse_error)
                  when MetaMod :: meta_mod(), Form :: func_form() | string().

Add a new exported function to MetaMod.

add_func(MetaMod, Func, Export)

-spec add_func(MetaMod, Func, Export) -> result(meta_mod(), parse_error)
                  when MetaMod :: meta_mod(), Func :: func_form() | string(), Export :: boolean().

Add Function to MetaMod and return the new meta_mod(). If Export is true, add Function to MetaMod's exports.

compile(MetaMod)

-spec compile(MetaMod :: meta_mod()) -> error_t(term()).

Compile MetaMod and load the resulting BEAM into the emulator.

compile(MetaMod, Options)

-spec compile(MetaMod, Options) -> error_t(term())
                 when MetaMod :: meta_mod(), Options :: [proplists:property()].

Compile MetaMod and load the resulting BEAM into the emulator. Options is a list of options as described in the compile module in the Erlang documentation. If an outdir is provided, write the .beam file to it.

curry(Form, Args)

-spec curry(Form :: func_form(), Args :: args()) -> result(func_form()).

Get the curried form for Form with Args. Here, "currying" involves replacing one or more of the function's leading arguments with predefined values.

curry(Module, Function, Arity, Args)

-spec curry(Module, Function, Arity, Args) -> result(func_form())
               when
                   Module :: module() | meta_mod(), Function :: atom(), Arity :: arity(), Args :: args().

Curry Module:Function/Arity with the given Args.

curry(Module, Function, Arity, Args, NewName)

-spec curry(Module, Function, Arity, Args, NewName) -> result(func_form())
               when
                   Module :: module() | meta_mod(),
                   Function :: atom(),
                   Arity :: arity(),
                   Args :: args(),
                   NewName :: atom().

Curry Module:Function/Aritywith the given Args, renaming it to NewName and return the renamed form.

curry_add(MetaMod, Form, Args)

-spec curry_add(MetaMod, Form, Args) -> result(meta_mod())
                   when MetaMod :: meta_mod(), Form :: func_form(), Args :: args().

Add Form curried with Args to MetaMod.

curry_add(MetaMod, Function, Arity, Args)

-spec curry_add(MetaMod, Function, Arity, Args) -> result(meta_mod())
                   when MetaMod :: meta_mod(), Function :: atom(), Arity :: arity(), Args :: args().

Add Function/Arity curried with Args to MetaMod.

curry_add(MetaMod, Function, Arity, Args, NewName)

-spec curry_add(MetaMod, Function, Arity, Args, NewName) -> Result
                   when
                       MetaMod :: meta_mod(),
                       Function :: atom(),
                       Arity :: arity(),
                       Args :: args(),
                       NewName :: atom(),
                       Result :: result(meta_mod(), parse_error).

Curry MetaMod:Function/Arity and add it to MetaMod as NewName.

curry_add(MetaMod, Module, Function, Arity, Args, NewName)

-spec curry_add(MetaMod, Module, Function, Arity, Args, NewName) -> Result
                   when
                       MetaMod :: meta_mod(),
                       Module :: module() | meta_mod(),
                       Function :: atom(),
                       Arity :: arity(),
                       Args :: args(),
                       NewName :: atom(),
                       Result :: result(meta_mod()).

Curry Module:Function/Arity and add it to MetaMod as NewName.

curry_replace(MetaMod, Form, Args)

-spec curry_replace(MetaMod, Form, Args) -> result(meta_mod())
                       when MetaMod :: meta_mod(), Form :: func_form(), Args :: args().

Replace the function represented by Form in MetaMod with its curried form.

curry_replace(MetaMod, Function, Arity, Args)

-spec curry_replace(MetaMod, Function, Arity, Args) -> result(meta_mod())
                       when MetaMod :: meta_mod(), Function :: atom(), Arity :: arity(), Args :: args().

Replace Function/Arity in MetaMod with its curried form.

embed_all(MetaMod, Values)

-spec embed_all(MetaMod, Values) -> NewMod
                   when
                       MetaMod :: meta_mod(),
                       Values :: [{Name :: atom(), Value :: term()}],
                       NewMod :: meta_mod().

Apply {@link embed_args/2} with Values to all forms in MetaMod. exports for functions whose arities change are preserved.

embed_args(Form, Vals)

-spec embed_args(Form, Vals) -> NewForm
                    when
                        Form :: func_form(),
                        Vals :: [{Name :: atom(), Value :: term()}],
                        NewForm :: func_form().

Replace the arguments of the function represented by Form, where the argument's Name matches an element from Vals with the corresponding Value.

embed_args(MetaMod, Function, Arity, Values)

-spec embed_args(MetaMod, Function, Arity, Values) -> result(meta_mod())
                    when
                        MetaMod :: meta_mod(),
                        Function :: atom(),
                        Arity :: arity(),
                        Values :: proplists:proplist().

embed_args(MetaMod, Function, Arity, Values, NewName)

-spec embed_args(MetaMod, Function, Arity, Values, NewName) -> Result
                    when
                        MetaMod :: meta_mod(),
                        Function :: atom(),
                        Arity :: arity(),
                        Values :: proplists:proplist(),
                        NewName :: atom(),
                        Result :: result(meta_mod()).

Apply {@link embed_args/2} to MetaMod:Function/Arity and add the resulting function to MetMod, after renaming it to NewName. See: rename/2.

extend(Parent, Child)

-spec extend(Parent, Child) -> NewChildMod
                when
                    Parent :: module() | meta_mod(),
                    Child :: module() | meta_mod(),
                    NewChildMod :: meta_mod().

Add aliases for Parent's functions missing from Child to Child. The new functions in Child are shallow, i.e. they have the name and arity of the corresponding functions in Parent, but instead of implementing their logic they call the Parent functions.

extend(Parent, Child, ArityDiff)

-spec extend(Parent, Child, ArityDiff) -> NewChildMod
                when
                    Parent :: module() | meta_mod(),
                    Child :: module() | meta_mod(),
                    ArityDiff :: non_neg_integer(),
                    NewChildMod :: meta_mod().

Similar to {@link extend/2}, with the addition of ArityDiff, which indicates the difference in arities <em>Smerl</em> should use when figuring out which functions to generate based on the modules' exports. This is sometimes useful when calling {@link extend/3} followed by {@link embed_all/2}.

extend(Parent, Child, ArityDiff, Options)

-spec extend(Parent, Child, ArityDiff, Options) -> NewChildMod
                when
                    Parent :: module() | meta_mod(),
                    Child :: module() | meta_mod(),
                    ArityDiff :: non_neg_integer(),
                    Options :: [proplists:property()],
                    NewChildMod :: meta_mod().

for_file(SrcFilePath)

for_file(SrcFilePath, IncludePaths)

for_file(SrcFilePath, IncludePaths, Macros)

-spec for_file(SrcFilePath, IncludePaths, Macros) -> Result
                  when
                      SrcFilePath :: file:filename(),
                      IncludePaths :: [file:filename()],
                      Macros :: [{module(), atom()}],
                      Result :: result(meta_mod(), invalid_module).

Create a meta_mod for a module from its source file.

for_module(ModuleName)

for_module(ModuleName, IncludePaths)

for_module(ModuleName, IncludePaths, Macros)

-spec for_module(ModuleName, IncludePaths, Macros) -> result(meta_mod)
                    when
                        ModuleName :: atom() | string(),
                        IncludePaths :: [string()],
                        Macros :: [{atom(), term()}].

Create a meta_mod tuple for an existing module. If ModuleName is a string, it is interpreted as a file name (this is the same as calling {@link for_file/3}). If ModuleName is an atom, <em>Smerl</em> attempts to find its abstract represtation either from its source file or from its .beam file directly (if it has been compiled with debug_info). If the abstract representation can't be found, this function returns an error.

The IncludePaths argument is used when ModuleName is a file name.

get_attribute(MetaMod, Key)

-spec get_attribute(MetaMod :: meta_mod(), Key :: atom()) -> result(term()).

Get the value of MetaMod's Key attribute.

get_export_all(MetaMod)

-spec get_export_all(MetaMod :: meta_mod()) -> boolean().

Get the export_all value for MetaMod.

get_exports(MetaMod)

-spec get_exports(MetaMod :: meta_mod()) -> exports().

Return the list of exports in the meta_mod.

get_forms(MetaMod)

-spec get_forms(MetaMod :: meta_mod()) -> func_forms().

Return the list of function forms in the meta_mod.

get_func(MetaMod, Function, Arity)

-spec get_func(MetaMod, Function, Arity) -> result(func_form())
                  when MetaMod :: meta_mod() | module(), Function :: atom(), Arity :: arity().

Attempt to get the func_form() for MetaMod:Function/Arity.

get_module(MetaMod)

-spec get_module(MetaMod :: meta_mod()) -> module().

Return the module name for the meta_mod.

has_func(MetaMod, Function, Arity)

-spec has_func(MetaMod, Function, Arity) -> boolean()
                  when MetaMod :: meta_mod(), Function :: atom(), Arity :: arity().

Check whether MetaMod has a function Function/Arity.

new(Module)

-spec new(Module :: module()) -> meta_mod().

Create a new meta_mod for a module with the given name.

remove_export(MetaMod, Function, Arity)

-spec remove_export(MetaMod, Function, Arity) -> NewMod
                       when
                           MetaMod :: meta_mod(),
                           Function :: atom(),
                           Arity :: arity(),
                           NewMod :: meta_mod().

Remove an export {Function, Arity} from the list of exports in MetaMod.

remove_func(MetaMod, Function, Arity)

-spec remove_func(MetaMod, Function, Arity) -> NewMod
                     when
                         MetaMod :: meta_mod(),
                         Function :: atom(),
                         Arity :: arity(),
                         NewMod :: meta_mod().

Try to remove Function from MetaMod. If the function exists, return the new meta_mod(). Otherwise, return MetaMod.

rename(Form, NewName)

-spec rename(Form :: func_form(), NewName :: atom()) -> func_form().

Change the name of the function represented by Form to NewName.

replace_func(MetaMod, Function)

-spec replace_func(MetaMod, Function) -> result(meta_mod())
                      when MetaMod :: meta_mod(), Function :: string() | func_form().

Replace an existing function with a new one. If a matching function doesn't exist, add Function to MetaMod. This is tantamount to calling {@link remove_func/3} followed by {@link add_func/2}.

set_export_all(MetaMod, Value)

-spec set_export_all(MetaMod, Value) -> NewMod
                        when MetaMod :: meta_mod(), Value :: boolean(), NewMod :: meta_mod().

Set the export_all value for MetaMod.

set_exports(MetaMod, Exports)

-spec set_exports(MetaMod, Exports) -> NewMod
                     when MetaMod :: meta_mod(), Exports :: exports(), NewMod :: meta_mod().

Set the MetaMod's export list to Exports.

set_forms(MetaMod, Forms)

-spec set_forms(MetaMod, Forms) -> NewMod
                   when MetaMod :: meta_mod(), Forms :: func_forms(), NewMod :: meta_mod().

set_module(MetaMod, NewName)

-spec set_module(MetaMod, NewName) -> NewMod
                    when MetaMod :: meta_mod(), NewName :: module(), NewMod :: meta_mod().

Set the meta_mod's module name.

to_src(MetaMod)

-spec to_src(MetaMod :: meta_mod()) -> Source :: string().

Return the pretty-printed source code for MetaMod.

to_src(MetaMod, Filename)

-spec to_src(MetaMod, Filename) -> error_t(term())
                when MetaMod :: meta_mod(), Filename :: file:filename().