Files
n8n-openai-adapter/lib/elixir/src/elixir_try.erl
T

196 lines
6.4 KiB
Erlang

-module(elixir_try).
-export([clauses/4]).
-include("elixir.hrl").
clauses(_Meta, Clauses, Return, S) ->
Catch = elixir_clauses:get_pairs('catch', Clauses, 'catch'),
Rescue = elixir_clauses:get_pairs(rescue, Clauses, rescue),
reduce_clauses(Rescue ++ Catch, [], S, Return, S).
reduce_clauses([H|T], Acc, SAcc, Return, S) ->
{TH, TS} = each_clause(H, Return, SAcc),
reduce_clauses(T, TH ++ Acc, elixir_scope:mergec(S, TS), Return, S);
reduce_clauses([], Acc, SAcc, _Return, _S) ->
{lists:reverse(Acc), SAcc}.
each_clause({'catch', Meta, Raw, Expr}, Return, S) ->
{Args, Guards} = elixir_clauses:extract_splat_guards(Raw),
Final = case Args of
[X] -> [throw, X, {'_', Meta, nil}];
[X,Y] -> [X, Y, {'_', Meta, nil}]
end,
Condition = [{'{}', Meta, Final}],
{TC, TS} = elixir_clauses:clause(?line(Meta), fun elixir_translator:translate_args/2,
Condition, Expr, Guards, Return, S),
{[TC], TS};
each_clause({rescue, Meta, [{in, _, [Left, Right]}], Expr}, Return, S) ->
{VarName, _, CS} = elixir_scope:build_var('_', S),
Var = {VarName, Meta, nil},
{Parts, Safe, FS} = rescue_guards(Meta, Var, Right, CS),
Body =
case Left of
{'_', _, Atom} when is_atom(Atom) ->
Expr;
_ ->
Normalized =
case Safe of
true -> Var;
false -> {{'.', Meta, ['Elixir.Exception', normalize]}, Meta, [error, Var]}
end,
prepend_to_block(Meta, {'=', Meta, [Left, Normalized]}, Expr)
end,
build_rescue(Meta, Parts, Body, Return, FS);
each_clause({rescue, Meta, _, _}, _Return, S) ->
elixir_errors:compile_error(Meta, S#elixir_scope.file, "invalid arguments for rescue in try");
each_clause({Key, Meta, _, _}, _Return, S) ->
elixir_errors:compile_error(Meta, S#elixir_scope.file, "invalid key ~ts in try", [Key]).
%% Helpers
build_rescue(Meta, Parts, Body, Return, S) ->
Matches = [Match || {Match, _} <- Parts],
{{clause, Line, TMatches, _, TBody}, TS} =
elixir_clauses:clause(?line(Meta), fun elixir_translator:translate_args/2,
Matches, Body, [], Return, S),
TClauses =
[begin
TArgs = [{tuple, Line, [{atom, Line, error}, TMatch, {var, Line, '_'}]}],
TGuards = elixir_clauses:guards(Line, Guards, [], TS),
{clause, Line, TArgs, TGuards, TBody}
end || {TMatch, {_, Guards}} <- lists:zip(TMatches, Parts)],
{TClauses, TS}.
%% Convert rescue clauses into guards.
rescue_guards(_, Var, {'_', _, _}, S) -> {[{Var, []}], false, S};
rescue_guards(Meta, Var, Aliases, S) ->
{Elixir, Erlang} = rescue_each_ref(Meta, Var, Aliases, [], [], S),
{ElixirParts, ES} =
case Elixir of
[] -> {[], S};
_ ->
{VarName, _, CS} = elixir_scope:build_var('_', S),
StructVar = {VarName, Meta, nil},
Map = {'%{}', Meta, [{'__struct__', StructVar}, {'__exception__', true}]},
Match = {'=', Meta, [Map, Var]},
Guards = [{erl(Meta, '=='), Meta, [StructVar, Mod]} || Mod <- Elixir],
{[{Match, Guards}], CS}
end,
ErlangParts =
case Erlang of
[] -> [];
_ -> [{Var, Erlang}]
end,
{ElixirParts ++ ErlangParts, ErlangParts == [], ES}.
%% Rescue each atom name considering their Erlang or Elixir matches.
%% Matching of variables is done with Erlang exceptions is done in
%% function for optimization.
rescue_each_ref(Meta, Var, [H|T], Elixir, Erlang, S) when is_atom(H) ->
case erl_rescue_guard_for(Meta, Var, H) of
false -> rescue_each_ref(Meta, Var, T, [H|Elixir], Erlang, S);
Expr -> rescue_each_ref(Meta, Var, T, [H|Elixir], [Expr|Erlang], S)
end;
rescue_each_ref(_, _, [], Elixir, Erlang, _) ->
{Elixir, Erlang}.
%% Handle erlang rescue matches.
erl_rescue_guard_for(Meta, Var, 'Elixir.UndefinedFunctionError') ->
{erl(Meta, '=='), Meta, [Var, undef]};
erl_rescue_guard_for(Meta, Var, 'Elixir.FunctionClauseError') ->
{erl(Meta, '=='), Meta, [Var, function_clause]};
erl_rescue_guard_for(Meta, Var, 'Elixir.SystemLimitError') ->
{erl(Meta, '=='), Meta, [Var, system_limit]};
erl_rescue_guard_for(Meta, Var, 'Elixir.ArithmeticError') ->
{erl(Meta, '=='), Meta, [Var, badarith]};
erl_rescue_guard_for(Meta, Var, 'Elixir.CondClauseError') ->
{erl(Meta, '=='), Meta, [Var, cond_clause]};
erl_rescue_guard_for(Meta, Var, 'Elixir.BadArityError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badarity));
erl_rescue_guard_for(Meta, Var, 'Elixir.BadFunctionError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badfun));
erl_rescue_guard_for(Meta, Var, 'Elixir.MatchError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badmatch));
erl_rescue_guard_for(Meta, Var, 'Elixir.CaseClauseError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, case_clause));
erl_rescue_guard_for(Meta, Var, 'Elixir.TryClauseError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, try_clause));
erl_rescue_guard_for(Meta, Var, 'Elixir.BadStructError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 3),
erl_record_compare(Meta, Var, badstruct));
erl_rescue_guard_for(Meta, Var, 'Elixir.ArgumentError') ->
erl_or(Meta,
{erl(Meta, '=='), Meta, [Var, badarg]},
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badarg)));
erl_rescue_guard_for(Meta, Var, 'Elixir.ErlangError') ->
IsNotTuple = {erl(Meta, 'not'), Meta, [{erl(Meta, is_tuple), Meta, [Var]}]},
IsException = {erl(Meta, '/='), Meta, [
{erl(Meta, element), Meta, [2, Var]}, '__exception__'
]},
erl_or(Meta, IsNotTuple, IsException);
erl_rescue_guard_for(_, _, _) ->
false.
%% Helpers
erl_tuple_size(Meta, Var, Size) ->
{erl(Meta, '=='), Meta, [{erl(Meta, tuple_size), Meta, [Var]}, Size]}.
erl_record_compare(Meta, Var, Expr) ->
{erl(Meta, '=='), Meta, [
{erl(Meta, element), Meta, [1, Var]},
Expr
]}.
prepend_to_block(_Meta, Expr, {'__block__', Meta, Args}) ->
{'__block__', Meta, [Expr|Args]};
prepend_to_block(Meta, Expr, Args) ->
{'__block__', Meta, [Expr, Args]}.
erl(Meta, Op) -> {'.', Meta, [erlang, Op]}.
erl_or(Meta, Left, Right) -> {'__op__', Meta, ['orelse', Left, Right]}.
erl_and(Meta, Left, Right) -> {'__op__', Meta, ['andalso', Left, Right]}.