@@ -67,8 +67,8 @@ replace([], Result, _Dict) ->
6767 lists :reverse (lists :flatten (Result ));
6868
6969replace (" $(" ++ Rest , Result , Dict ) ->
70- {ok , VarName , RemainsAfterVariable } = read_var_name (Rest ),
71- Value = maps :get (VarName , Dict ),
70+ {ok , VarName , Transform , RemainsAfterVariable } = read_var_name (Rest ),
71+ Value = eval ( VarName , Transform , maps :get (VarName , Dict ) ),
7272 replace (RemainsAfterVariable , [lists :reverse (Value ) | Result ], Dict );
7373
7474replace ([C | RestTemplate ], Result , Dict ) ->
@@ -81,13 +81,13 @@ match([], [], D, _) ->
8181 D ;
8282
8383match (" $(" ++ Rest , String , D , Arg ) ->
84- {ok , VarName , RemainsAfterVariable } = read_var_name (Rest ),
84+ {ok , VarName , Transform , RemainsAfterVariable } = read_var_name (Rest ),
8585 case maps :get (VarName , D , undefined ) of
8686 undefined -> % % if we don't have an existing binding we try all possible bindings
87- bind (VarName , [], RemainsAfterVariable , String , D , Arg );
87+ bind ({ VarName , Transform } , [], RemainsAfterVariable , String , D , Arg );
8888
8989 CurrentValue -> % % if we have an existing binding we substitute the value of the variable and try to match
90- match (lists :flatten ([CurrentValue | RemainsAfterVariable ]), String , D , Arg )
90+ match (lists :flatten ([eval ( VarName , Transform , CurrentValue ) | RemainsAfterVariable ]), String , D , Arg )
9191 end ;
9292
9393match ([C | TemplateRest ], [C | StringRest ], D , Arg ) ->
@@ -98,43 +98,69 @@ match(_, _, _, Arg) ->
9898
9999
100100
101- bind (VarName , Value , [], [], D , _ ) ->
102- D #{VarName => lists :reverse (Value )};
101+ bind ({ VarName , T } , Value , [], [], D , _ ) ->
102+ D #{VarName => eval ( VarName , T , lists :reverse (Value ) )};
103103
104- bind (VarName , _ , [], String , D , _ ) when length (String ) > 0 ->
105- D #{VarName => String };
104+ bind ({ VarName , T } , _ , [], String , D , _ ) when length (String ) > 0 ->
105+ D #{VarName => eval ( VarName , T , String ) };
106106
107- bind (VarName , Value , Template , [], D , Arg ) when length (Template ) > 0 ->
108- NewD = D #{VarName => lists :reverse (Value )},
107+ bind ({ VarName , T } , Value , Template , [], D , Arg ) when length (Template ) > 0 ->
108+ NewD = D #{VarName => eval ( VarName , T , lists :reverse (Value ) )},
109109 match (Template , [], NewD , Arg );
110110
111- bind (VarName , AccValue , String , String , D , _ ) ->
112- D #{VarName => lists :reverse (AccValue )};
111+ bind ({ VarName , T } , AccValue , String , String , D , _ ) ->
112+ D #{VarName => eval ( VarName , T , lists :reverse (AccValue ) )};
113113
114- bind (VarName , AccValue , Template , [C | StringRest ] = String , D , Arg ) ->
115- NewD = D #{VarName => lists :reverse (AccValue )},
114+ bind ({ VarName , T } , AccValue , Template , [C | StringRest ] = String , D , Arg ) ->
115+ NewD = D #{VarName => eval ( VarName , T , lists :reverse (AccValue ) )},
116116 case match (Template , String , NewD , Arg ) of
117117 {error , _Reason } ->
118- bind (VarName , [C | AccValue ], Template , StringRest , D , Arg );
118+ bind ({ VarName , T } , [C | AccValue ], Template , StringRest , D , Arg );
119119
120120 YetAnotherD ->
121121 YetAnotherD
122122 end .
123123
124124
125+ eval (_ , identity , Val ) ->
126+ Val ;
127+ eval (VarName , Body , Val ) ->
128+ FunStr = " fun (" ++ VarName ++ " ) -> " ++ Body ++ " end." ,
129+ {ok , Tokens , _ } = erl_scan :string (FunStr ),
130+ {ok , [Form ]} = erl_parse :parse_exprs (Tokens ),
131+ {value , Fun , _ } = erl_eval :expr (Form , erl_eval :new_bindings ()),
132+ Fun (Val ).
133+
134+
125135
126136% % reads out the variable name from a string where the string starts after the "$(" part of the variable
137+ % % a variable name could either be on the simple form $(NAME) or include a function body to be evaluated to substitute
138+ % % the value of the variable, such as $(NAME|lists:reverse(NAME))
127139read_var_name (String ) ->
128140 read_var_name (String , []).
129141
130142read_var_name ([], _Res ) ->
131143 {error , eof };
132- read_var_name ([$) | Remain ], Res ) ->
133- {ok , lists :reverse (Res ), Remain };
144+ read_var_name (" |" ++ Remain , Res ) ->
145+ read_fun_body (Res , Remain , [], 0 );
146+ read_var_name (" )" ++ Remain , Res ) ->
147+ {ok , lists :reverse (Res ), identity , Remain };
134148read_var_name ([C | Remain ], Res ) ->
135149 read_var_name (Remain , [C | Res ]).
136150
137151
152+ read_fun_body (_VarName , [], _Res , _PBalance ) ->
153+ {error , eof };
154+ read_fun_body (VarName , " (" ++ Remain , Res , PBalance ) ->
155+ read_fun_body (VarName , Remain , [$( | Res ], PBalance + 1 );
156+ read_fun_body (VarName , " )" ++ Remain , Res , PBalance ) when PBalance > 0 ->
157+ read_fun_body (VarName , Remain , [$) | Res ], PBalance - 1 );
158+ read_fun_body (VarName , " )" ++ Remain , Res , 0 ) ->
159+ {ok , lists :reverse (VarName ), lists :reverse (Res ), Remain };
160+ read_fun_body (VarName , [C | Remain ], Res , PBalance ) ->
161+ read_fun_body (VarName , Remain , [C | Res ], PBalance ).
162+
163+
138164
139165% %% EUNIT tests %%%%
140166-ifdef (TEST ).
@@ -242,4 +268,48 @@ match_with_variable_pattern_test() ->
242268 ? assertEqual ([" A" ], maps :keys (D )),
243269 ? assertEqual (" $(A)" , maps :get (" A" , D )).
244270
271+ match_with_complex_variable_test () ->
272+ D = match (" $(A|lists:reverse(A))" , " ab" ),
273+ ? assertEqual ([" A" ], maps :keys (D )),
274+ ? assertEqual (" ba" , maps :get (" A" , D )).
275+
276+ match_palindrom_test () ->
277+ D = match (" $(A)$(A|lists:reverse(A))" , " olassalo" ),
278+ ? assertEqual ([" A" ], maps :keys (D )),
279+ ? assertEqual (" olas" , maps :get (" A" , D )).
280+
281+ match_non_palindrom_test () ->
282+ ? assertEqual ({error ,{no_match ,{" $(A)$(A|lists:reverse(A))" ," abcb" }}},
283+ match (" $(A)$(A|lists:reverse(A))" , " abcb" )).
284+
285+ match_reversed_vars_test () ->
286+ D = match (" $(A)a$(A|lists:reverse(A))" , " 12a21" ),
287+ ? assertEqual (" 12" , maps :get (" A" , D )).
288+
289+ match_reversed2_vars_test () ->
290+ D = match (" $(A|lists:reverse(A))a$(A)" , " 12a21" ),
291+ ? assertEqual (" 21" , maps :get (" A" , D )).
292+
293+ match_complex_vars_test () ->
294+ ? assertEqual (#{" A" => " Stefan" },
295+ template :match (" $(A)$(A|lists:reverse(string:to_upper(A)))" , " StefanNAFETS" )).
296+
297+
298+ % % read_var_name
299+ read_simple_var_test () ->
300+ ? assertEqual ({ok , " A" , identity , " " }, read_var_name (" A)" )),
301+ ? assertEqual ({ok , " A" , identity , " rest" }, read_var_name (" A)rest" )).
302+
303+ read_complex_var_test () ->
304+ ? assertEqual ({ok , " A" , " r(A)" , " " }, read_var_name (" A|r(A))" )),
305+ ? assertEqual ({ok , " A" , " r(A)" , " rest" }, read_var_name (" A|r(A))rest" )).
306+
307+ read_bad_complex_var_test () ->
308+ ? assertEqual ({error , eof }, read_var_name (" A|r(A" )).
309+
310+ % % eval test
311+ eval_test () ->
312+ ? assertEqual (" a" , eval (" A" , identity , " a" )),
313+ ? assertEqual (" cba" , eval (" A" , " lists:reverse(A)" , " abc" )).
314+
245315-endif .
0 commit comments