[erlang-questions] more flexible code generation

Ulf Wiger ulf@REDACTED
Tue Dec 18 16:06:11 CET 2012


On 17 Dec 2012, at 10:38, Tim Watson wrote:

> Ulf - some feedback: this is totally AWESOME!
> 
> Damn I need to find time to go and refactor at least three projects to take advantage of this now.... :D

Thanks for the encouraging feedback. :)

I've just pushed some additions for improving error reporting, which I would also like feedback on.

Handling errors in parse transforms has been a bit tricky. My latest approach is to use a new function, parse_trans:return(Forms, Context), which checks for the presence of {error, Info} or {warning, Info} tuples in the abstract tree. It will then extract them and produce a return that the compiler is comfortable with.

I have also added a function, parse_trans:format_exception(Class, Error), which can be used to produce a less user-hostile exception trace.

To illustrate: I've started working on a module for code generation from YANG specs:

https://github.com/tonyrog/yang/blob/master/src/yang_codegen.erl

I got some horrible exception dumps, and was motivated to address the problem. The parse_transform/2 function in parse_trans_codegen.erl now looks like this:

parse_transform(Forms, Options) ->
    Context = parse_trans:initial_context(Forms, Options),
    {NewForms, _} =
	parse_trans:do_depth_first(
	  fun xform_fun/4, _Acc = Forms, Forms, Context),
    parse_trans:return(parse_trans:revert(NewForms), Context).

where the parse_trans:return(Forms, Context) is new.

A crash in the code generation now looks like:

==> yang (compile)
src/yang_codegen.erl:71: exception error: no case clause matching match_expr
  in function  parse_trans_codegen:gen_function_/4 (src/parse_trans_codegen.erl, line 189)
  in call from parse_trans_codegen:gen_function/5 (src/parse_trans_codegen.erl, line 181)
  in call from parse_trans_codegen:xform_fun/4 (src/parse_trans_codegen.erl, line 156)
ERROR: compile failed while processing /Users/uwiger/FL/git/yang: rebar_abort
make: *** [compile] Error 1

…which, by parse_transform standards, is pretty decent. Note that we get the line info in the original source code.

The error-trapping code in parse_trans_codegen.erl looks like this:

https://github.com/uwiger/parse_trans/blob/master/src/parse_trans_codegen.erl#L180

gen_function(NameF, FunF, L0, L, Acc) ->
    try gen_function_(NameF, FunF, L, Acc)
    catch
	error:E ->
	    ErrStr = parse_trans:format_exception(error, E),
	    {error, {L0, ?MODULE, ErrStr}}
    end.

Now, I *could* rework the existing functions in parse_trans to produce error info in this way. It won't happen immediately, since I have other, more urgent, things on my plate, but - would anyone have objections to this?

It's not entirely trivial. If I have the existing functions add {error, I} and {warning, I} tuples in the form tree, and the parse_transform() functions don't end with a call to parse_trans:return/2, the linter will crash, which is neither pretty, nor informative. If the old functions call parse_trans:return/2 automatically, this will mess up code that makes successive calls to them.

BW compatibility sucks, as Robert Virding once put it. I am open to suggestions.

BR,
Ulf W

> 
> On 16 Dec 2012, at 11:39, Ulf Wiger wrote:
> 
>> 
>> I made a little addition to my parse_trans library, in parse_trans_codegen:
>> 
>> The 'original' method for generating code for a function was e.g.:
>> 
>> g(Name, V) ->
>>   codegen:gen_function(
>>     Name,
>>     fun(L) ->
>>             member({'$var',V}, L)
>>     end).
>> 
>> Where the {'$var', V} notation is a way to insert a reference to a variable, rather than using the variable itself. 
>> 
>> To illustrate, let's first create a little pretty-printer fun:
>> 
>> 1> PP = fun(F) -> io:fwrite("~s~n", [erl_pp:form(F)]) end.
>> #Fun<erl_eval.6.82930912>
>> 
>> 
>> 2> PP(ex_codegen:g(foo,17)).
>> foo(L) ->
>>   member(17, L).
>> 
>> There are a few other, similar, functions, but in some cases, this is still too restrictive. A few days ago, I added support for specifying a more dynamic pattern:
>> 
>> k(L) ->
>>   codegen:gen_function(
>>     lcf,
>>     [fun({'$var',X}) ->
>>              {'$var',Y}
>>      end || {X, Y}  <- L]).
>> 
>> The list comprehension results in a list of clauses based on data resolved at run-time. For example:
>> 
>> 3> PP(ex_codegen:k([ {"a", {1,2,3}}, {"b", {4,5,6}} ])).
>> lcf("a") ->
>>   {1,2,3};
>> lcf("b") ->
>>   {4,5,6}.
>> 
>> https://github.com/uwiger/parse_trans/blob/master/doc/parse_trans_codegen.md
>> 
>> Feedback is welcome.
>> 
>> BR,
>> Ulf W
>> 
>> Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.
>> http://feuerlabs.com
>> 
>> 
>> 
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://erlang.org/mailman/listinfo/erlang-questions
> 

Ulf Wiger, Co-founder & Developer Advocate, Feuerlabs Inc.
http://feuerlabs.com






More information about the erlang-questions mailing list