@@ -67,8 +67,8 @@ replace([], Result, _Dict) ->
67
67
lists :reverse (lists :flatten (Result ));
68
68
69
69
replace (" $(" ++ 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 ) ),
72
72
replace (RemainsAfterVariable , [lists :reverse (Value ) | Result ], Dict );
73
73
74
74
replace ([C | RestTemplate ], Result , Dict ) ->
@@ -81,13 +81,13 @@ match([], [], D, _) ->
81
81
D ;
82
82
83
83
match (" $(" ++ Rest , String , D , Arg ) ->
84
- {ok , VarName , RemainsAfterVariable } = read_var_name (Rest ),
84
+ {ok , VarName , Transform , RemainsAfterVariable } = read_var_name (Rest ),
85
85
case maps :get (VarName , D , undefined ) of
86
86
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 );
88
88
89
89
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 )
91
91
end ;
92
92
93
93
match ([C | TemplateRest ], [C | StringRest ], D , Arg ) ->
@@ -98,43 +98,69 @@ match(_, _, _, Arg) ->
98
98
99
99
100
100
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 ) )};
103
103
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 ) };
106
106
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 ) )},
109
109
match (Template , [], NewD , Arg );
110
110
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 ) )};
113
113
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 ) )},
116
116
case match (Template , String , NewD , Arg ) of
117
117
{error , _Reason } ->
118
- bind (VarName , [C | AccValue ], Template , StringRest , D , Arg );
118
+ bind ({ VarName , T } , [C | AccValue ], Template , StringRest , D , Arg );
119
119
120
120
YetAnotherD ->
121
121
YetAnotherD
122
122
end .
123
123
124
124
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
+
125
135
126
136
% % 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))
127
139
read_var_name (String ) ->
128
140
read_var_name (String , []).
129
141
130
142
read_var_name ([], _Res ) ->
131
143
{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 };
134
148
read_var_name ([C | Remain ], Res ) ->
135
149
read_var_name (Remain , [C | Res ]).
136
150
137
151
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
+
138
164
139
165
% %% EUNIT tests %%%%
140
166
-ifdef (TEST ).
@@ -242,4 +268,48 @@ match_with_variable_pattern_test() ->
242
268
? assertEqual ([" A" ], maps :keys (D )),
243
269
? assertEqual (" $(A)" , maps :get (" A" , D )).
244
270
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
+
245
315
-endif .
0 commit comments