<div dir="ltr"><div>Hello all,<br><br>Recently I've went and began implementing a concept preprocessor that extends Erlang's pattern matching and guard syntax with constructs to make bundles of patterns and guard sequences reusable, one of which resembles a form of user-defined guard.<br><br>A few quick examples:<br><br>--------------------------<br><br><span style="font-family:monospace,monospace"> %% example1.erl<br> %%<br> -module(example1).<br> -export([ test/0 ]).<br> -auto_include_udg([ \\non_empty_message/3, is_uep_displayable_uuid/1 ]).<br> -record(message, { flags = 0, data = <<>>, uuid = <<>>}).<br> <br> test() -><br> receive<br> Message \\ non_empty_message(UUID, MsgFlags, MsgLine)<br> when MsgFlags /= 0, is_uep_displayable_uuid(UUID)<br> orelse UUID =:= <<>> -><br> io:format("From UUID: ~s~n", [UUID]),<br> io:format("Message: ~s", [MsgLine])<br> after 1000 -> ok<br> end.<br></span><br>--------------------------<br><br><span style="font-family:monospace,monospace"> %% example2.erl<br> %%<br> -module(example2).<br> -export([ test/1 ]).<br> -auto_include_udg([ \\integer_in_second_place/1 ]).<br> <br> test(Compound \\ integer_in_second_place(Val)) -> Val.<br><br></span>--------------------------<br><br>For these examples the definitions for the pattern templates and guards are:<br><br>--------------------------<br><br><span style="font-family:monospace,monospace"> %% non_empty_message.erg<br> %%<br> -user_defined_guard(non_empty_message, [<br> \\non_empty_message/3 ]).<br> <br> (#message{ flags = MsgFlags, data = MsgLine, uuid = UUID })<br> \\ non_empty_message(UUID, MsgFlags, MsgLine)<br> when is_binary(MsgLine), MsgLine =/= <<>>.<br></span><br>--------------------------<br><br><span style="font-family:monospace,monospace"> %% displayable_uuid.erg<br> %%<br> -user_defined_guard(displayable_uuid, [<br> is_uep_displayable_uuid/6, is_uep_displayable_uuid/1,<br> \\displayable_uuid/5, \\displayable_uuid/0 ]).<br> <br> (UUID \\ displayable_uuid(_,_,_,_,_)) \\ displayable_uuid().<br> <br> (<<UUID_Part_1:8/bytes,"-",<br> UUID_Part_2:4/bytes,"-",<br> UUID_Part_3:4/bytes,"-",<br> UUID_Part_4:4/bytes,"-",<br> UUID_Part_5:12/bytes>>) \\ displayable_uuid(<br> UUID_Part_1, UUID_Part_2, UUID_Part_3, UUID_Part_4, UUID_Part_5).<br> <br> is_uep_displayable_uuid(UUID \\ displayable_uuid()).<br> <br> is_uep_displayable_uuid(<br> UUID \\ displayable_uuid(UUID_Part_1, UUID_Part_2, UUID_Part_3, UUID_Part_4, UUID_Part_5),<br> UUID_Part_1,UUID_Part_2,UUID_Part_3,UUID_Part_4,UUID_Part_5).<br></span><br>--------------------------<br><br><span style="font-family:monospace,monospace"> %% integer_in_second_place.erg<br> %%<br> -user_defined_guard(integer_in_second_place, [<br> \\integer_in_second_place/1 ]).<br> <br> ([_,Value|_]) \\ integer_in_second_place(Value) := is_integer(Value);<br> ({_,Value}) \\ integer_in_second_place(Value) := is_integer(Value);<br> ({_,Value,_}) \\ integer_in_second_place(Value) when is_integer(Value).<br></span><br>--------------------------<br><br>A more thorough overview and description, as well as the preprocessor source as it currently exists, can be found at the repository at:<br><br> <a href="https://github.com/elblake/udg">https://github.com/elblake/udg</a><br><br>The beginnings of a test suite is present which does some tests over a series of source file scenarios, the loading of definition source files, and modules requiring user input, one of which tests all files in a directory recursively that preprocessed files are identical token-wise with the original file, intended to see if the preprocessor splits and recombines alternatives within function heads, cases, funs, receives, trys and ifs correctly as well an indication if it could handle Erlang code in a real world setting. I've passed that test over my erlang installation's lib directory and
they all pass. However the preprocessor does skip over definitions that
contain ? in some places of the function head since it doesn't implement
macro expansion.<br><br>And there is also another test early in design which randomly generates arbitrary Erlang (albeit of a very small subset) source code with randomly selected patterns and guards.<br><br>For the convenience of testing it out, an erlc_udg module takes as command line input an erl file to preprocess it before compiling it with compile:file.<br><br>I also apologize in advance for using the term "user-defined guard" for the concept construct as that was what I was using as a working name during implementation, I could have used another term but I couldn't come up with one as of yet.<br><br>The preprocessor is a very early implementation and not complete in many cases, some types of expressions are not parsed for the .erg definitions for example, erroneous conditions in input source code simply causes run-time errors without any presentation, and some scenarios with binaries are a work in progress.<br><br>The (:=) operator was chosen to delimit the tail guard expression simply because I wanted an erl_scan available operator that would go before the disjunction operator to maintain a similarity to full head disjunctions of functions, so having semicolons directly after 'when' wouldn't do, and using (->) which normally hints to a sequence of general purpose expressions for this purpose would be confusing.<br><br>Input is welcome.<br><br><br></div></div>