"Dynamic" module generation with compile-time macros
Over on the Erlang Questions mailing list, Jacob Perkins asked:
"I have a few files that are basically lists of words. What I want to do is generate a module whose functions will return each list of words. So if I have two files named "adjectives" and "nouns", then the generated module (let's call it 'grammar') should have two functions, adjectives() and nouns(), that return their respective list of words.
How can I do this?"
If I had some method of running Erlang code at compile-time - and I do - then I could write a program to slurp in those adjective and noun files and generate the appropriate functions.
adjectives.txt:
this
that
these
nouns.txt:
apple
banana
carrot
grammar.erl:
-module(grammar).
-export([nouns/0, adjectives/0]).
-compile({parse_transform, emp2}).
-macro({grammar_macro, generate, [nouns]}).
-macro({grammar_macro, generate, [adjectives]}).
grammar_macro.erl:
-module(grammar_macro).
-export([generate/1]).
generate(NameFun) ->
{ok,Fd} = file:open(atom_to_list(NameFun) ++ ".txt", [read]),
generate(Fd, NameFun, []).
generate(Fd, NameFun, Words) ->
case io:get_line(Fd, "") of
eof ->
ok=file:close(Fd),
io_lib:format("~w() -> ~w.~n", [NameFun, lists:reverse(Words)]);
Word -> generate(Fd, NameFun, [Word | Words])
end.
And in the Erlang shell:
1> c(grammar_macro), c(grammar).
{ok,grammar}
2> grammar:nouns().
["apple\n","banana\n","carrot\n"]
3> grammar:adjectives().
["this\n","that\n","these\n"]