%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2021. 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(trycatch_SUITE).

-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
	 init_per_testcase/2,end_per_testcase/2,
	 init_per_suite/1,end_per_suite/1,
	 basic/1,lean_throw/1,try_of/1,try_after/1,%after_bind/1,
	 catch_oops/1,after_oops/1,eclectic/1,rethrow/1,
	 nested_of/1,nested_catch/1,nested_after/1]).

-include_lib("common_test/include/ct.hrl").

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap,{minutes,1}}].

all() -> 
    cases().

groups() -> 
    [].

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


cases() -> 
    [basic, lean_throw, try_of, try_after, catch_oops,
     after_oops, eclectic, rethrow, nested_of, nested_catch,
     nested_after].

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

end_per_testcase(_Case, _Config) ->
    ok.

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

end_per_suite(Config) when is_list(Config) ->
    ok.

basic(Conf) when is_list(Conf) ->
    2 =
	try my_div(4, 2)
	catch
            Class:Reason -> {Class,Reason}
	end,
    error =
        try my_div(1, 0)
        catch
            error:badarith -> error
        end,
    error =
        try 1/0
        catch
            error:badarith -> error
        end,
    ok =
        try my_add(53, atom)
        catch
            error:badarith -> ok
        end,
    exit_nisse =
        try exit(nisse)
	catch
            exit:nisse -> exit_nisse
        end,
    ok =
        try throw(kalle)
        catch
            kalle -> ok
        end,

    %% Try some stuff where the compiler will optimize away the try.

    V = id({a,variable}),
    V = try V catch nisse -> error end,
    42 = try 42 catch nisse -> error end,
    [V] = try [V] catch nisse -> error end,
    {ok,V} = try {ok,V} catch nisse -> error end,

    %% Same idea, but use an after too.

    V = try V catch nisse -> error after after_call() end,
    after_clean(),
    42 = try 42 after after_call() end,
    after_clean(),
    [V] = try [V] catch nisse -> error after after_call() end,
    after_clean(),
    {ok,V} = try {ok,V} after after_call() end,

    %% Try/of
    ok = try V of
	     {a,variable} -> ok
	 catch nisse -> error
	 end,

    ok.

after_call() ->
    put(basic, after_was_called).

after_clean() ->
    after_was_called = erase(basic).

lean_throw(Conf) when is_list(Conf) ->
    {throw,kalle} =
        try throw(kalle)
        catch
            Kalle -> {throw,Kalle}
        end,
    {exit,kalle} =
        try exit(kalle)
        catch
            Throw1 -> {throw,Throw1};
	    exit:Reason1 -> {exit,Reason1}
        end,
    {exit,kalle} =
        try exit(kalle)
        catch
	    exit:Reason2 -> {exit,Reason2};
            Throw2 -> {throw,Throw2}
        end,
    {exit,kalle} =
        try try exit(kalle)
            catch
                Throw3 -> {throw,Throw3}
            end
        catch
            exit:Reason3 -> {exit,Reason3}
        end,
    ok.

try_of(Conf) when is_list(Conf) ->
    {ok,{some,content}} =
	try_of_1({value,{good,{some,content}}}),
    {error,[other,content]} =
	try_of_1({value,{bad,[other,content]}}),
    {caught,{exit,{ex,it,[reason]}}} =
	try_of_1({exit,{ex,it,[reason]}}),
    {caught,{throw,[term,{in,a,{tuple}}]}} =
	try_of_1({throw,[term,{in,a,{tuple}}]}),
    {caught,{error,[bad,arg]}} =
	try_of_1({error,[bad,arg]}),
    {caught,{error,badarith}} =
	try_of_1({'div',{1,0}}),
    {caught,{error,badarith}} =
	try_of_1({'add',{a,0}}),
    {caught,{error,badarg}} =
	try_of_1({'abs',x}),
    {caught,{error,function_clause}} =
	try_of_1(illegal),
    {error,{try_clause,{some,other_garbage}}} =
	try try_of_1({value,{some,other_garbage}})
        catch error:Reason -> {error,Reason}
        end,
    ok.

try_of_1(X) ->
    try foo(X) of
        {good,Y} -> {ok,Y};
	{bad,Y} -> {error,Y}
    catch
	Class:Reason ->
	    {caught,{Class,Reason}}
    end.

try_after(Conf) when is_list(Conf) ->
    {{ok,[some,value],undefined},finalized} =
	try_after_1({value,{ok,[some,value]}},finalized),
    {{error,badarith,undefined},finalized} =
	try_after_1({'div',{1,0}},finalized),
    {{error,badarith,undefined},finalized} =
	try_after_1({'add',{1,a}},finalized),
    {{error,badarg,undefined},finalized} =
	try_after_1({'abs',a},finalized),
    {{error,[the,{reason}],undefined},finalized} =
	try_after_1({error,[the,{reason}]},finalized),
    {{throw,{thrown,[reason]},undefined},finalized} =
	try_after_1({throw,{thrown,[reason]}},finalized),
    {{exit,{exited,{reason}},undefined},finalized} =
	try_after_1({exit,{exited,{reason}}},finalized),
    {{error,function_clause,undefined},finalized} =
	try_after_1(function_clause,finalized),
    ok =
	try try_after_1({'add',{1,1}}, finalized)
        catch
            error:{try_clause,2} -> ok
	end,
    finalized = erase(try_after),
    ok =
        try try foo({exit,[reaso,{n}]})
            after put(try_after, finalized)
            end
        catch
            exit:[reaso,{n}] -> ok
        end,
    ok.

try_after_1(X, Y) ->
    erase(try_after),
    Try =
        try foo(X) of
	    {ok,Value} -> {ok,Value,get(try_after)}
        catch
	    Reason -> {throw,Reason,get(try_after)};
	    error:Reason -> {error,Reason,get(try_after)};
	    exit:Reason ->  {exit,Reason,get(try_after)}
        after
	    put(try_after, Y)
        end,
    {Try,erase(try_after)}.

-ifdef(begone).

after_bind(Conf) when is_list(Conf) ->
    V = [make_ref(),self()|value],
    {value,{value,V}} =
	after_bind_1({value,V}, V, {value,V}),
    ok.

after_bind_1(X, V, Y) ->
    try
        Try =
            try foo(X) of
                V -> value
            catch
                C1:V -> {caught,C1}
            after
                After = foo(Y)
	    end,
        {Try,After}
    of
        V -> {value,V}
    catch
        C:D -> {caught,{C,D}}
    end.

-endif.

catch_oops(Conf) when is_list(Conf) ->
	V = {v,[a,l|u],{e},self()},
	{value,V} = catch_oops_1({value,V}),
	{value,1} = catch_oops_1({'div',{1,1}}),
	{error,badarith} = catch_oops_1({'div',{1,0}}),
	{error,function_clause} = catch_oops_1(function_clause),
	{throw,V} = catch_oops_1({throw,V}),
	{exit,V} = catch_oops_1({exit,V}),
	ok.

catch_oops_1(X) ->
	Ref = make_ref(),
	try try foo({error,Ref})
	    catch
		error:Ref ->
		    foo(X)
	    end of
	    Value -> {value,Value}
	catch
	    Class:Data -> {Class,Data}
	end.



after_oops(Conf) when is_list(Conf) ->
    V = {self(),make_ref()},
    {{value,V},V} = after_oops_1({value,V}, {value,V}),
    {{exit,V},V} = after_oops_1({exit,V}, {value,V}),
    {{error,V},undefined} = after_oops_1({value,V}, {error,V}),
    {{error,function_clause},undefined} =
	after_oops_1({exit,V}, function_clause),
    ok.

after_oops_1(X, Y) ->
    erase(after_oops),
    Try =
        try try foo(X)
            after
                put(after_oops, foo(Y))
            end of
            V -> {value,V}
        catch
            C:D -> {C,D}
        end,
    {Try,erase(after_oops)}.



eclectic(Conf) when is_list(Conf) ->
    V = {make_ref(),3.1415926535,[[]|{}]},
    {{value,{value,V},V},V} =
	eclectic_1({foo,{value,{value,V}}}, undefined, {value,V}),
    {{'EXIT',{V,[{?MODULE,foo,_,_}|_]}},V} =
	eclectic_1({catch_foo,{error,V}}, undefined, {value,V}),
    {{error,{exit,V},{'EXIT',V}},V} =
	eclectic_1({foo,{error,{exit,V}}}, error, {value,V}),
    {{value,{value,V},V},{'EXIT',{badarith,[{?MODULE,my_add,_,_}|_]}}} =
	eclectic_1({foo,{value,{value,V}}}, undefined, {'add',{0,a}}),
    {{'EXIT',V},V} =
	eclectic_1({catch_foo,{exit,V}}, undefined, {throw,V}),
    {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,_,_}|_]}}},
     {'EXIT',V}} =
	eclectic_1({foo,{error,{'div',{1,0}}}}, error, {exit,V}),
    {{{error,V},{'EXIT',{V,[{?MODULE,foo,_,_}|_]}}},{'EXIT',V}} =
	eclectic_1({catch_foo,{throw,{error,V}}}, undefined, {exit,V}),
    %%
    {{value,{value,{value,V},V}},V} =
	eclectic_2({value,{value,V}}, undefined, {value,V}),
    {{value,{throw,{value,V},V}},V} =
	eclectic_2({throw,{value,V}}, throw, {value,V}),
    {{caught,{'EXIT',V}},undefined} =
	eclectic_2({value,{value,V}}, undefined, {exit,V}),
    {{caught,{'EXIT',{V,[{?MODULE,foo,_,_}|_]}}},undefined} =
	eclectic_2({error,{value,V}}, throw, {error,V}),
    {{caught,{'EXIT',{badarg,[{erlang,abs,[V],_}|_]}}},V} =
	eclectic_2({value,{'abs',V}}, undefined, {value,V}),
    {{caught,{'EXIT',{badarith,[{?MODULE,my_add,_,_}|_]}}},V} =
	eclectic_2({exit,{'add',{0,a}}}, exit, {value,V}),
    {{caught,{'EXIT',V}},undefined} =
	eclectic_2({value,{error,V}}, undefined, {exit,V}),
    {{caught,{'EXIT',{V,[{?MODULE,foo,_,_}|_]}}},undefined} =
	eclectic_2({throw,{'div',{1,0}}}, throw, {error,V}),
    ok.

eclectic_1(X, C, Y) ->
    erase(eclectic),
    Done = make_ref(),
    Try =
        try case X of
		{catch_foo,V} -> catch {Done,foo(V)};
		{foo,V} -> {Done,foo(V)}
	    end of
            {Done,D} -> {value,D,catch foo(D)};
	    {'EXIT',_}=Exit -> Exit;
	    D -> {D,catch foo(D)}
        catch
            C:D -> {C,D,catch foo(D)}
        after
            put(eclectic, catch foo(Y))
        end,
    {Try,erase(eclectic)}.

eclectic_2(X, C, Y) ->
    Done = make_ref(),
    erase(eclectic),
    Catch =
	case
            catch
		{Done,
		 try foo(X) of
		     V -> {value,V,foo(V)}
		 catch
		     C:D -> {C,D,foo(D)}
		 after
		     put(eclectic, foo(Y))
		 end} of
		{Done,Z} -> {value,Z};
		Z -> {caught,Z}
	    end,
    {Catch,erase(eclectic)}.

rethrow(Conf) when is_list(Conf) ->
    V = {a,[b,{c,self()},make_ref]},
    {value2,value1} =
	rethrow_1({value,V}, V),
    {caught2,{error,V}} =
	rethrow_2({error,V}, undefined),
    {caught2,{exit,V}} =
	rethrow_1({exit,V}, error),
    {caught2,{throw,V}} =
	rethrow_1({throw,V}, undefined),
    {caught2,{throw,V}} =
	rethrow_2({throw,V}, undefined),
    {caught2,{error,badarith}} =
	rethrow_1({'add',{0,a}}, throw),
    {caught2,{error,function_clause}} =
	rethrow_2(function_clause, undefined),
    {caught2,{error,{try_clause,V}}} =
	rethrow_1({value,V}, exit),
    {value2,{caught1,V}} =
	rethrow_1({error,V}, error),
    {value2,{caught1,V}} =
	rethrow_1({exit,V}, exit),
    {value2,caught1} =
	rethrow_2({throw,V}, V),
    ok.

rethrow_1(X, C1) ->
    try try foo(X) of
            C1 -> value1
        catch
            C1:D1 -> {caught1,D1}
        end of
        V2 -> {value2,V2}
    catch
        C2:D2 -> {caught2,{C2,D2}}
    end.

rethrow_2(X, C1) ->
    try try foo(X) of
            C1 -> value1
        catch
            C1 -> caught1 % Implicit class throw:
        end of
        V2 -> {value2,V2}
    catch
        C2:D2 -> {caught2,{C2,D2}}
    end.



nested_of(Conf) when is_list(Conf) ->
    V = {[self()|make_ref()],1.4142136},
    {{value,{value1,{V,x2}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_of_1({{value,{V,x1}},void,{V,x1}},
		    {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{throw,{V,x2}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_of_1({{value,{V,x1}},void,{V,x1}},
		    {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     undefined,
     {V,x4},
     finalized} =
	nested_of_1({{value,{V,x1}},void,{V,x1}},
		    {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     undefined,
     undefined,
     finalized} =
	nested_of_1({{value,{V,x1}},void,{V,x1}},
		    {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}),
    %%
    {{caught,{error,{try_clause,{V,x1}}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_of_1({{value,{V,x1}},void,try_clause},
		    void, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{exit,{V,x3}}},
     undefined,
     {V,x4},
     finalized} =
	nested_of_1({{value,{V,x1}},void,try_clause},
		    void, {exit,{V,x3}}, {value,{V,x4}}),
    {{caught,{throw,{V,x4}}},
     undefined,
     undefined,
     finalized} =
	nested_of_1({{value,{V,x1}},void,try_clause},
		    void, {exit,{V,x3}}, {throw,{V,x4}}),
    %%
    {{value,{caught1,{V,x2}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_of_1({{error,{V,x1}},error,{V,x1}},
		    {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_of_1({{error,{V,x1}},error,{V,x1}},
		    {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     undefined,
     {V,x4},
     finalized} =
	nested_of_1({{error,{V,x1}},error,{V,x1}},
		    {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}),
    {{caught,{error,badarg}},
     undefined,
     undefined,
     finalized} =
	nested_of_1({{error,{V,x1}},error,{V,x1}},
		    {'add',{1,c}}, {'div',{17,0}}, {'abs',V}),
    %%
    {{caught,{error,badarith}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_of_1({{'add',{2,c}},rethrow,void},
		    void, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarg}},
     undefined,
     {V,x4},
     finalized} =
	nested_of_1({{'add',{2,c}},rethrow,void},
		    void, {'abs',V}, {value,{V,x4}}),
    {{caught,{error,function_clause}},
     undefined,
     undefined,
     finalized} =
	nested_of_1({{'add',{2,c}},rethrow,void},
		    void, {'abs',V}, function_clause),
    ok.

nested_of_1({X1,C1,V1},
	    X2, X3, X4) ->
    erase(nested3),
    erase(nested4),
    erase(nested),
    Self = self(),
    Try =
	try
            try self()
            of
                Self ->
                    try
                        foo(X1)
	            of
	                V1 -> {value1,foo(X2)}
                    catch
                        C1:V1 -> {caught1,foo(X2)}
	            after
                        put(nested3, foo(X3))
                    end
            after
                put(nested4, foo(X4))
            end
        of
            V -> {value,V}
        catch
            C:D -> {caught,{C,D}}
        after
            put(nested, finalized)
	end,
    {Try,erase(nested3),erase(nested4),erase(nested)}.



nested_catch(Conf) when is_list(Conf) ->
    V = {[make_ref(),1.4142136,self()]},
    {{value,{value1,{V,x2}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_catch_1({{value,{V,x1}},void,{V,x1}},
		       {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{throw,{V,x2}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_catch_1({{value,{V,x1}},void,{V,x1}},
		       {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     undefined,
     {V,x4},
     finalized} =
	nested_catch_1({{value,{V,x1}},void,{V,x1}},
		       {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     undefined,
     undefined,
     finalized} =
	nested_catch_1({{value,{V,x1}},void,{V,x1}},
		       {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}),
    %%
    {{caught,{error,{try_clause,{V,x1}}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_catch_1({{value,{V,x1}},void,try_clause},
		       void, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{exit,{V,x3}}},
     undefined,
     {V,x4},
     finalized} =
	nested_catch_1({{value,{V,x1}},void,try_clause},
		       void, {exit,{V,x3}}, {value,{V,x4}}),
    {{caught,{throw,{V,x4}}},
     undefined,
     undefined,
     finalized} =
	nested_catch_1({{value,{V,x1}},void,try_clause},
		       void, {exit,{V,x3}}, {throw,{V,x4}}),
    %%
    {{value,{caught1,{V,x2}}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_catch_1({{error,{V,x1}},error,{V,x1}},
		       {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_catch_1({{error,{V,x1}},error,{V,x1}},
		       {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarith}},
     undefined,
     {V,x4},
     finalized} =
	nested_catch_1({{error,{V,x1}},error,{V,x1}},
		       {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}),
    {{caught,{error,badarg}},
     undefined,
     undefined,
     finalized} =
	nested_catch_1({{error,{V,x1}},error,{V,x1}},
		       {'add',{1,c}}, {'div',{17,0}}, {'abs',V}),
    %%
    {{caught,{error,badarith}},
     {V,x3},
     {V,x4},
     finalized} =
	nested_catch_1({{'add',{2,c}},rethrow,void},
		       void, {value,{V,x3}}, {value,{V,x4}}),
    {{caught,{error,badarg}},
     undefined,
     {V,x4},
     finalized} =
	nested_catch_1({{'add',{2,c}},rethrow,void},
		       void, {'abs',V}, {value,{V,x4}}),
    {{caught,{error,function_clause}},
     undefined,
     undefined,
     finalized} =
	nested_catch_1({{'add',{2,c}},rethrow,void},
		       void, {'abs',V}, function_clause),
    ok.

nested_catch_1({X1,C1,V1},
	       X2, X3, X4) ->
    erase(nested3),
    erase(nested4),
    erase(nested),
    Throw = make_ref(),
    Try =
	try
            try throw(Throw)
            catch
		Throw ->
                    try
                        foo(X1)
	            of
	                V1 -> {value1,foo(X2)}
                    catch
                        C1:V1 -> {caught1,foo(X2)}
	            after
                        put(nested3, foo(X3))
                    end
            after
                put(nested4, foo(X4))
            end
        of
            V -> {value,V}
        catch
            C:D -> {caught,{C,D}}
        after
            put(nested, finalized)
	end,
    {Try,erase(nested3),erase(nested4),erase(nested)}.

nested_after(Conf) when is_list(Conf) ->
    V = [{make_ref(),1.4142136,self()}],
    {value,
     {V,x3},
     {value1,{V,x2}},
     finalized} =
	nested_after_1({{value,{V,x1}},void,{V,x1}},
		       {value,{V,x2}}, {value,{V,x3}}),
    {{caught,{error,{V,x2}}},
     {V,x3},
     undefined,
     finalized} =
	nested_after_1({{value,{V,x1}},void,{V,x1}},
		       {error,{V,x2}}, {value,{V,x3}}),
    {{caught,{exit,{V,x3}}},
     undefined,
     undefined,
     finalized} =
	nested_after_1({{value,{V,x1}},void,{V,x1}},
		       {error,{V,x2}}, {exit,{V,x3}}),
    %%
    {{caught,{error,{try_clause,{V,x1}}}},
     {V,x3},
     undefined,
     finalized} =
	nested_after_1({{value,{V,x1}},void,try_clause},
		       void, {value,{V,x3}}),
    {{caught,{error,badarith}},
     undefined,
     undefined,
     finalized} =
	nested_after_1({{value,{V,x1}},void,try_clause},
		       void, {'div',{17,0}}),
    %%
    {value,
     {V,x3},
     {caught1,{V,x2}},
     finalized} =
	nested_after_1({{throw,{V,x1}},throw,{V,x1}},
		       {value,{V,x2}}, {value,{V,x3}}),
    {{caught,{error,badarith}},
     {V,x3},
     undefined,
     finalized} =
	nested_after_1({{throw,{V,x1}},throw,{V,x1}},
		       {'add',{a,b}}, {value,{V,x3}}),
    {{caught,{error,badarg}},
     undefined,
     undefined,
     finalized} =
	nested_after_1({{throw,{V,x1}},throw,{V,x1}},
		       {'add',{a,b}}, {'abs',V}),
    %%
    {{caught,{throw,{V,x1}}},
     {V,x3},
     undefined,
     finalized} =
	nested_after_1({{throw,{V,x1}},rethrow,void},
		       void, {value,{V,x3}}),
    {{caught,{error,badarith}},
     undefined,
     undefined,
     finalized} =
	nested_after_1({{throw,{V,x1}},rethrow,void},
		       void, {'div',{1,0}}),
    ok.

nested_after_1({X1,C1,V1},
	       X2, X3) ->
    erase(nested3),
    erase(nested4),
    erase(nested),
    Self = self(),
    Try =
	try
            try self()
            after
                After =
                    try
                        foo(X1)
	            of
	                V1 -> {value1,foo(X2)}
                    catch
                        C1:V1 -> {caught1,foo(X2)}
	            after
                        put(nested3, foo(X3))
                    end,
                put(nested4, After)
            end
        of
            Self -> value
        catch
            C:D -> {caught,{C,D}}
        after
            put(nested, finalized)
	end,
    {Try,erase(nested3),erase(nested4),erase(nested)}.

foo({value,Value}) -> Value;
foo({'div',{A,B}}) ->
    my_div(A, B);
foo({'add',{A,B}}) ->
    my_add(A, B);
foo({'abs',X}) ->
    my_abs(X);
foo({error,Error}) ->
    erlang:error(Error);
foo({throw,Throw}) ->
    erlang:throw(Throw);
foo({exit,Exit}) ->
    erlang:exit(Exit);
foo({raise,{Class,Reason}}) ->
    erlang:raise(Class, Reason).
%%foo(function_clause) -> % must not be defined!

my_div(A, B) ->
    A div B.

my_add(A, B) ->
    A + B.

my_abs(X) -> abs(X).

id(I) -> I.
