Skip to content

Commit

Permalink
Teach the debugger to handle the maybe expression
Browse files Browse the repository at this point in the history
  • Loading branch information
bjorng committed Aug 29, 2023
1 parent 0418c10 commit 0ec0dec
Show file tree
Hide file tree
Showing 4 changed files with 332 additions and 0 deletions.
30 changes: 30 additions & 0 deletions lib/debugger/src/dbg_ieval.erl
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,25 @@ expr({'orelse',Line,E1,E2}, Bs0, Ieval) ->
exception(error, {badarg,Val}, Bs, Ieval)
end;

%% Maybe statement without else
expr({'maybe',Line,Es}, Bs, Ieval) ->
try
seq(Es, Bs, Ieval#ieval{line=Line})
catch
throw:{bad_maybe_match,Val} ->
{value,Val,Bs}
end;

%% Maybe statement with else
expr({'maybe',Line,Es,Cs}, Bs, Ieval) ->
try seq(Es, Bs, Ieval#ieval{line=Line}) of
{value,Val,_} ->
{value,Val,Bs}
catch
throw:{bad_maybe_match,Val} ->
case_clauses(Val, Cs, Bs, else_clause, Ieval#ieval{line=Line})
end;

%% Matching expression
expr({match,Line,Lhs,Rhs0}, Bs0, Ieval0) ->
Ieval = Ieval0#ieval{line=Line},
Expand All @@ -766,6 +785,17 @@ expr({match,Line,Lhs,Rhs0}, Bs0, Ieval0) ->
exception(error, {badmatch,Rhs}, Bs1, Ieval)
end;

%% Conditional match expression (?=)
expr({maybe_match,Line,Lhs,Rhs0}, Bs0, Ieval0) ->
Ieval = Ieval0#ieval{line=Line},
{value,Rhs,Bs1} = expr(Rhs0, Bs0, Ieval#ieval{top=false}),
case match(Lhs, Rhs, Bs1) of
{match,Bs} ->
{value,Rhs,Bs};
nomatch ->
throw({bad_maybe_match,Rhs})
end;

%% Construct a fun
expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) ->
Arity = length(element(3,hd(Cs))),
Expand Down
11 changes: 11 additions & 0 deletions lib/debugger/src/dbg_iload.erl
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,13 @@ expr({'receive',Anno,Cs0,To0,ToEs0}, Lc, St) ->
ToEs1 = exprs(ToEs0, Lc, St),
Cs1 = icr_clauses(Cs0, Lc, St),
{'receive',ln(Anno),Cs1,To1,ToEs1};
expr({'maybe',Anno,Es0}, Lc, St) ->
Es1 = exprs(Es0, Lc, St),
{'maybe',ln(Anno),Es1};
expr({'maybe',Anno,Es0,{'else',_ElseAnno,Cs0}}, Lc, St) ->
Es1 = exprs(Es0, Lc, St),
Cs1 = icr_clauses(Cs0, Lc, St),
{'maybe',ln(Anno),Es1,Cs1};
expr({'fun',Anno,{clauses,Cs0}}, _Lc, St) ->
%% New R10B-2 format (abstract_v2).
Cs = fun_clauses(Cs0, St),
Expand Down Expand Up @@ -619,6 +626,10 @@ expr({match,Anno,P0,E0}, _Lc, St) ->
E1 = expr(E0, false, St),
P1 = pattern(P0, St),
{match,ln(Anno),P1,E1};
expr({maybe_match,Anno,P0,E0}, _Lc, St) ->
E1 = expr(E0, false, St),
P1 = pattern(P0, St),
{maybe_match,ln(Anno),P1,E1};
expr({op,Anno,Op,A0}, _Lc, St) ->
A1 = expr(A0, false, St),
{op,ln(Anno),Op,[A1]};
Expand Down
1 change: 1 addition & 0 deletions lib/debugger/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ MODULES= \
lc_SUITE \
line_number_SUITE \
map_SUITE \
maybe_SUITE \
overridden_bif_SUITE \
record_SUITE \
trycatch_SUITE \
Expand Down
290 changes: 290 additions & 0 deletions lib/debugger/test/maybe_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2023. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

-module(maybe_SUITE).
-feature(maybe_expr, enable).
-include_lib("common_test/include/ct.hrl").

-export([all/0, groups/0, init_per_suite/1, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2]).
-export([basic/1, nested/1]).

all() ->
[{group,p}].

groups() ->
[{p,[parallel],
[basic,nested]}].

init_per_testcase(_Case, Config) ->
test_lib:interpret(?MODULE),
Config.

end_per_testcase(_Case, _Config) ->
ok.

init_per_suite(Config) ->
test_lib:interpret(?MODULE),
true = lists:member(?MODULE, int:interpreted()),
Config.

end_per_suite(_Config) ->
ok.

-record(value, {v}).

basic(_Config) ->
{ok,42,fish} = basic_1(0, #{0 => {ok,42}, 42 => {ok,fish}}),
error = basic_1(0, #{0 => {ok,42}, 42 => {error,whatever}}),
error = basic_1(0, #{0 => {ok,42}, 42 => error}),
error = basic_1(0, #{0 => error}),
error = basic_1(0, #{0 => {error,whatever}}),
some_value = basic_1(0, #{0 => #value{v=some_value}}),
{'EXIT',{{else_clause,something_wrong},[_|_]}} = catch basic_1(0, #{0 => something_wrong}),

{ok,life,"universe",everything} = basic_2(0, #{0 => {ok,life},
life => "universe",
"universe" => {ok,everything}}),
error = basic_2(0, #{0 => {ok,life},
life => "universe",
"universe" => error}),
{'EXIT',{{badmatch,not_a_list},[_|_]}} = catch basic_2(0, #{0 => {ok,life},
life => not_a_list}),
{'EXIT',{{else_clause,not_ok},[_|_]}} = catch basic_2(0, #{0 => {ok,life},
life => "universe",
"universe" => not_ok}),
{'EXIT',{{else_clause,not_ok},[_|_]}} = catch basic_2(0, #{0 => not_ok}),

{ok,42,fish,dolphins} = basic_3(0, #{0 => {ok,42}, 42 => {ok,fish},
fish => {ok,#value{v=dolphins}}}),
{error,whatever} = basic_3(0, #{0 => {ok,42}, 42 => {error,whatever}}),
failed = basic_3(0, #{0 => {ok,42}, 42 => failed}),
failed_early = basic_3(0, #{0 => failed_early}),

y = maybe nomatch ?= id(x) else _ -> y end,
y = maybe nomatch ?= id(x) else _ -> x, y end,

x = maybe nomatch ?= id(x) else E1 -> E1 end,

6 = maybe X1 = 2+2, X1+2 end,
6 = maybe X2 = 2+2, X2+2 else {error, T} -> T end,
{"llo", "hello", "hello"} = maybe Y1 = "he"++X3=Z1 ?= "hello", {X3,Y1,Z1} end,
{"llo", "hello", "llo"} = maybe Y2 = "he"++(X4=Z2) ?= "hello", {X4,Y2,Z2} end,

whatever = maybe
AlwaysMatching ?= id(whatever),
AlwaysMatching
else
E2 -> E2
end,

<<0>> = basic_4(id({<<0>>})),

ok.

basic_1(V0, M) ->
Res = basic_1a(V0, M),
{wrapped,Res} = basic_1b(V0, M),
{wrapped,Res} = basic_1c(V0, M),
Res.

basic_1a(V0, M) ->
maybe
{ok,V1} ?= do_something(V0, M),
{ok,V2} ?= do_something(V1, M),
{ok,V1,V2}
else
{error,_} ->
error;
error ->
error;
#value{v=V} ->
V
end.

basic_1b(V0, M) ->
Result =
maybe
{ok,V1} ?= do_something(V0, M),
{ok,V2} ?= do_something(V1, M),
{ok,V1,V2}
else
{error,_} ->
error;
error ->
error;
#value{v=V} ->
V
end,
{wrapped,Result}.

basic_1c(V0, M) ->
OK = id(ok),
Error = id(error),
Result =
maybe
{OK,V1} ?= do_something(V0, M),
{OK,V2} ?= do_something(V1, M),
{OK,V1,V2}
else
{Error,_} ->
Error;
Error ->
Error;
#value{v=V} ->
V
end,
{wrapped,Result}.

basic_2(V0, M) ->
Res = basic_2a(V0, M),
{wrapped,Res} = basic_2b(V0, M),
Res.

basic_2a(V0, M) ->
maybe
{ok,V1} ?= do_something(V0, M),
V2 = [_|_] = do_something(V1, M),
{ok,V3} ?= do_something(V2, M),
{ok,V1,V2,V3}
else
{error,_} ->
error;
error ->
error;
#value{v=V} ->
V
end.

basic_2b(V0, M) ->
Result =
maybe
{ok,V1} ?= do_something(V0, M),
V2 = [_|_] = do_something(V1, M),
{ok,V3} ?= do_something(V2, M),
{ok,V1,V2,V3}
else
{error,_} ->
error;
error ->
error;
#value{v=V} ->
V
end,
_ = id(0),
{wrapped,Result}.

basic_3(V0, M) ->
Res = basic_3a(V0, M),
{wrapped,Res} = basic_3b(V0, M),
Res.

basic_3a(V0, M) ->
maybe
{ok,V1} ?= do_something(V0, M),
{ok,V2} ?= do_something(V1, M),
{ok,#value{v=V3}} ?= do_something(V2, M),
{ok,V1,V2,V3}
end.

basic_3b(V0, M) ->
Result =
maybe
{ok,V1} ?= do_something(V0, M),
{ok,V2} ?= do_something(V1, M),
{ok,#value{v=V3}} ?= do_something(V2, M),
{ok,V1,V2,V3}
end,
{wrapped,Result}.

basic_4({X}) ->
maybe
<<_:(ok)>> ?= X
end.

nested(_Config) ->
{outer_fail,not_ok} = nested_1(0, #{0 => not_ok}),
{x,{error,inner}} = nested_1(0, #{0 => {ok,x}, x => {error,inner}}),
{outer_fail,{unexpected,not_error}} = nested_1(0, #{0 => {ok,x}, x => not_error}),
ok.

nested_1(V0, M) ->
Res = nested_1a(V0, M),
{wrapped,Res} = nested_1b(V0, M),
{wrapped,Res} = nested_1c(V0, M),
Res.

nested_1a(V0, M) ->
maybe
{ok,V1} ?= do_something(V0, M),
V2 = {error,_} ?=
maybe
{error, _} ?= id(do_something(V1, M))
else
Unexpected -> {unexpected, Unexpected}
end,
{V1,V2}
else
Res -> {outer_fail,Res}
end.

nested_1b(V0, M) ->
Result =
maybe
{ok,V1} ?= do_something(V0, M),
V2 = {error,_} ?=
maybe
{error, _} ?= id(do_something(V1, M))
else
Unexpected -> {unexpected, Unexpected}
end,
{V1,V2}
else
Res -> {outer_fail,Res}
end,
{wrapped,Result}.

nested_1c(V0, M) ->
Result =
maybe
R ?= maybe
{ok,V1} ?= do_something(V0, M),
{error,_} = V2 ?=
maybe
{error, _} ?= id(do_something(V1, M))
else
Unexpected -> {unexpected, Unexpected}
end,
{V1,V2}
else
Res -> {outer_fail,Res}
end,
R
else
Var -> Var
end,
{wrapped,Result}.

%% Utility functions.

do_something(V, M) ->
map_get(id(V), M).

id(X) -> X.

0 comments on commit 0ec0dec

Please sign in to comment.