1-module(elixir_quote). 2-export([escape/3, linify/3, linify_with_context_counter/3, build/6, quote/6, has_unquotes/1, fun_to_quoted/1]). 3-export([dot/5, tail_list/3, list/2, validate_runtime/2]). %% Quote callbacks 4 5-include("elixir.hrl"). 6-define(defs(Kind), Kind == def; Kind == defp; Kind == defmacro; Kind == defmacrop; Kind == '@'). 7-define(lexical(Kind), Kind == import; Kind == alias; Kind == require). 8-compile({inline, [keyfind/2, keystore/3, keydelete/2, keynew/3, do_tuple_linify/5]}). 9 10-record(elixir_quote, { 11 line=false, 12 file=nil, 13 context=nil, 14 vars_hygiene=true, 15 aliases_hygiene=true, 16 imports_hygiene=true, 17 unquote=true, 18 generated=false 19}). 20 21build(Meta, Line, File, Context, Unquote, Generated) -> 22 Acc0 = [], 23 {ELine, Acc1} = validate_compile(Meta, line, Line, Acc0), 24 {EFile, Acc2} = validate_compile(Meta, file, File, Acc1), 25 {EContext, Acc3} = validate_compile(Meta, context, Context, Acc2), 26 validate_runtime(unquote, Unquote), 27 validate_runtime(generated, Generated), 28 29 Q = #elixir_quote{ 30 line=ELine, 31 file=EFile, 32 unquote=Unquote, 33 context=EContext, 34 generated=Generated 35 }, 36 37 {Q, Acc3}. 38 39validate_compile(_Meta, line, Value, Acc) when is_boolean(Value) -> 40 {Value, Acc}; 41validate_compile(_Meta, file, nil, Acc) -> 42 {nil, Acc}; 43validate_compile(Meta, Key, Value, Acc) -> 44 case is_valid(Key, Value) of 45 true -> 46 {Value, Acc}; 47 false -> 48 Var = {Key, Meta, ?MODULE}, 49 Call = {{'.', Meta, [?MODULE, validate_runtime]}, Meta, [Key, Value]}, 50 {Var, [{'=', Meta, [Var, Call]} | Acc]} 51 end. 52 53validate_runtime(Key, Value) -> 54 case is_valid(Key, Value) of 55 true -> 56 Value; 57 58 false -> 59 erlang:error( 60 'Elixir.ArgumentError':exception( 61 <<"invalid runtime value for option :", (erlang:atom_to_binary(Key, utf8))/binary, 62 " in quote, got: ", ('Elixir.Kernel':inspect(Value))/binary>> 63 ) 64 ) 65 end. 66 67is_valid(line, Line) -> is_integer(Line); 68is_valid(file, File) -> is_binary(File); 69is_valid(context, Context) -> is_atom(Context) andalso (Context /= nil); 70is_valid(generated, Generated) -> is_boolean(Generated); 71is_valid(unquote, Unquote) -> is_boolean(Unquote). 72 73%% Apply the line from site call on quoted contents. 74%% Receives a Key to look for the default line as argument. 75linify(0, _Key, Exprs) -> 76 Exprs; 77linify(Line, Key, Exprs) when is_integer(Line) -> 78 LinifyMeta = linify_meta(Line, Key), 79 do_linify(LinifyMeta, Exprs, nil). 80 81%% Same as linify but also considers the context counter. 82linify_with_context_counter(Line, Var, Exprs) when is_integer(Line) -> 83 LinifyMeta = linify_meta(Line, line), 84 do_linify(LinifyMeta, Exprs, Var). 85 86do_linify(LinifyMeta, {quote, Meta, [_ | _] = Args}, {Receiver, Counter} = Var) 87 when is_list(Meta) -> 88 NewMeta = 89 case keyfind(context, Meta) == {context, Receiver} of 90 true -> keynew(counter, Meta, Counter); 91 false -> Meta 92 end, 93 do_tuple_linify(LinifyMeta, NewMeta, quote, Args, Var); 94 95do_linify(LinifyMeta, {Left, Meta, Receiver}, {Receiver, Counter} = Var) 96 when is_atom(Left), is_list(Meta), Left /= '_' -> 97 do_tuple_linify(LinifyMeta, keynew(counter, Meta, Counter), Left, Receiver, Var); 98 99do_linify(LinifyMeta, {Lexical, Meta, [_ | _] = Args}, {_, Counter} = Var) 100 when ?lexical(Lexical); Lexical == '__aliases__' -> 101 do_tuple_linify(LinifyMeta, keynew(counter, Meta, Counter), Lexical, Args, Var); 102 103do_linify(LinifyMeta, {Left, Meta, Right}, Var) when is_list(Meta) -> 104 do_tuple_linify(LinifyMeta, Meta, Left, Right, Var); 105 106do_linify(LinifyMeta, {Left, Right}, Var) -> 107 {do_linify(LinifyMeta, Left, Var), do_linify(LinifyMeta, Right, Var)}; 108 109do_linify(LinifyMeta, List, Var) when is_list(List) -> 110 [do_linify(LinifyMeta, X, Var) || X <- List]; 111 112do_linify(_, Else, _) -> Else. 113 114do_tuple_linify(LinifyMeta, Meta, Left, Right, Var) -> 115 {do_linify(LinifyMeta, Left, Var), LinifyMeta(Meta), do_linify(LinifyMeta, Right, Var)}. 116 117linify_meta(0, line) -> fun(Meta) -> Meta end; 118linify_meta(Line, line) -> fun(Meta) -> keynew(line, Meta, Line) end; 119linify_meta(Line, keep) -> 120 fun(Meta) -> 121 case lists:keytake(keep, 1, Meta) of 122 {value, {keep, {_, Int}}, MetaNoFile} -> 123 [{line, Int} | keydelete(line, MetaNoFile)]; 124 _ -> 125 keynew(line, Meta, Line) 126 end 127 end. 128 129%% Some expressions cannot be unquoted at compilation time. 130%% This function is responsible for doing runtime unquoting. 131dot(Meta, Left, Right, Args, Context) -> 132 annotate(dot(Meta, Left, Right, Args), Context). 133 134dot(Meta, Left, {'__aliases__', _, Args}, nil) -> 135 {'__aliases__', Meta, [Left | Args]}; 136 137dot(Meta, Left, Right, nil) when is_atom(Right) -> 138 case atom_to_list(Right) of 139 "Elixir." ++ _ -> 140 {'__aliases__', Meta, [Left, Right]}; 141 _ -> 142 {{'.', Meta, [Left, Right]}, [{no_parens, true} | Meta], []} 143 end; 144 145dot(Meta, Left, {Right, _, Context}, nil) when is_atom(Right), is_atom(Context) -> 146 {{'.', Meta, [Left, Right]}, [{no_parens, true} | Meta], []}; 147 148dot(Meta, Left, {Right, _, Args}, nil) when is_atom(Right) -> 149 {{'.', Meta, [Left, Right]}, Meta, Args}; 150 151dot(_Meta, _Left, Right, nil) -> 152 argument_error(<<"expected unquote after dot to return an atom, an alias or a quoted call, got: ", 153 ('Elixir.Macro':to_string(Right))/binary>>); 154 155dot(Meta, Left, Right, Args) when is_atom(Right) -> 156 {{'.', Meta, [Left, Right]}, Meta, Args}; 157 158dot(Meta, Left, {Right, _, Context}, Args) when is_atom(Right), is_atom(Context) -> 159 {{'.', Meta, [Left, Right]}, Meta, Args}; 160 161dot(_Meta, _Left, Right, _Args) -> 162 argument_error(<<"expected unquote after dot with args to return an atom or a quoted call, got: ", 163 ('Elixir.Macro':to_string(Right))/binary>>). 164 165list(Left, Right) when is_list(Right) -> 166 validate_list(Left), 167 Left ++ Right. 168 169tail_list(Left, Right, Tail) when is_list(Right), is_list(Tail) -> 170 validate_list(Left), 171 Tail ++ Left ++ Right; 172 173tail_list(Left, Right, Tail) when is_list(Left) -> 174 validate_list(Left), 175 [H | T] = lists:reverse(Tail ++ Left), 176 lists:reverse([{'|', [], [H, Right]} | T]). 177 178validate_list(List) when is_list(List) -> 179 ok; 180validate_list(List) when not is_list(List) -> 181 argument_error(<<"expected a list with quoted expressions in unquote_splicing/1, got: ", 182 ('Elixir.Kernel':inspect(List))/binary>>). 183 184argument_error(Message) -> 185 error('Elixir.ArgumentError':exception([{message, Message}])). 186 187%% Annotates the AST with context and other info. 188%% 189%% Note we need to delete the counter because linify 190%% adds the counter recursively, even inside quoted 191%% expressions, so we need to clean up the forms to 192%% allow them to get a new counter on the next expansion. 193 194annotate({Def, Meta, [{H, M, A} | T]}, Context) when ?defs(Def) -> 195 {Def, Meta, [{H, keystore(context, M, Context), A} | T]}; 196annotate({{'.', _, [_, Def]} = Target, Meta, [{H, M, A} | T]}, Context) when ?defs(Def) -> 197 {Target, Meta, [{H, keystore(context, M, Context), A} | T]}; 198 199annotate({Lexical, Meta, [_ | _] = Args}, Context) when ?lexical(Lexical) -> 200 NewMeta = keystore(context, keydelete(counter, Meta), Context), 201 {Lexical, NewMeta, Args}; 202annotate(Tree, _Context) -> Tree. 203 204has_unquotes({unquote, _, [_]}) -> true; 205has_unquotes({unquote_splicing, _, [_]}) -> true; 206has_unquotes({{'.', _, [_, unquote]}, _, [_]}) -> true; 207has_unquotes({Var, _, Ctx}) when is_atom(Var), is_atom(Ctx) -> false; 208has_unquotes({Name, _, Args}) when is_list(Args) -> 209 has_unquotes(Name) orelse lists:any(fun has_unquotes/1, Args); 210has_unquotes({Left, Right}) -> 211 has_unquotes(Left) orelse has_unquotes(Right); 212has_unquotes(List) when is_list(List) -> 213 lists:any(fun has_unquotes/1, List); 214has_unquotes(_Other) -> false. 215 216%% Escapes the given expression. It is similar to quote, but 217%% lines are kept and hygiene mechanisms are disabled. 218escape(Expr, Kind, Unquote) -> 219 do_quote(Expr, #elixir_quote{ 220 line=true, 221 file=nil, 222 vars_hygiene=false, 223 aliases_hygiene=false, 224 imports_hygiene=false, 225 unquote=Unquote 226 }, Kind). 227 228%% fun_to_quoted 229 230fun_to_quoted(Function) -> 231 Meta = [], 232 {module, Module} = erlang:fun_info(Function, module), 233 {name, Name} = erlang:fun_info(Function, name), 234 {arity, Arity} = erlang:fun_info(Function, arity), 235 {'&', Meta, [{'/', Meta, [{{'.', Meta, [Module, Name]}, [{no_parens, true} | Meta], []}, Arity]}]}. 236 237%% Quotes an expression and return its quoted Elixir AST. 238 239quote(_Meta, {unquote_splicing, _, [_]}, _Binding, #elixir_quote{unquote=true}, _, _) -> 240 argument_error(<<"unquote_splicing only works inside arguments and block contexts, " 241 "wrap it in parens if you want it to work with one-liners">>); 242 243quote(Meta, Expr, Binding, Q, Prelude, E) -> 244 Context = Q#elixir_quote.context, 245 246 Vars = [{'{}', [], 247 ['=', [], [ 248 {'{}', [], [K, Meta, Context]}, 249 V 250 ]] 251 } || {K, V} <- Binding], 252 253 Quoted = do_quote(Expr, Q, E), 254 255 WithVars = case Vars of 256 [] -> Quoted; 257 _ -> {'{}', [], ['__block__', [], Vars ++ [Quoted]]} 258 end, 259 260 case Prelude of 261 [] -> WithVars; 262 _ -> {'__block__', [], Prelude ++ [WithVars]} 263 end. 264 265%% Actual quoting and helpers 266 267do_quote({quote, Meta, [Arg]}, Q, E) -> 268 TArg = do_quote(Arg, Q#elixir_quote{unquote=false}, E), 269 270 NewMeta = case Q of 271 #elixir_quote{vars_hygiene=true, context=Context} -> keystore(context, Meta, Context); 272 _ -> Meta 273 end, 274 275 {'{}', [], [quote, meta(NewMeta, Q), [TArg]]}; 276 277do_quote({quote, Meta, [Opts, Arg]}, Q, E) -> 278 TOpts = do_quote(Opts, Q, E), 279 TArg = do_quote(Arg, Q#elixir_quote{unquote=false}, E), 280 281 NewMeta = case Q of 282 #elixir_quote{vars_hygiene=true, context=Context} -> keystore(context, Meta, Context); 283 _ -> Meta 284 end, 285 286 {'{}', [], [quote, meta(NewMeta, Q), [TOpts, TArg]]}; 287 288do_quote({unquote, _Meta, [Expr]}, #elixir_quote{unquote=true}, _) -> 289 Expr; 290 291%% Aliases 292 293do_quote({'__aliases__', Meta, [H | T]} = Alias, #elixir_quote{aliases_hygiene=true} = Q, E) when is_atom(H) and (H /= 'Elixir') -> 294 Annotation = 295 case elixir_aliases:expand(Alias, E) of 296 Atom when is_atom(Atom) -> Atom; 297 Aliases when is_list(Aliases) -> false 298 end, 299 AliasMeta = keystore(alias, keydelete(counter, Meta), Annotation), 300 do_quote_tuple('__aliases__', AliasMeta, [H | T], Q, E); 301 302%% Vars 303 304do_quote({Name, Meta, nil}, #elixir_quote{vars_hygiene=true, imports_hygiene=true} = Q, E) when is_atom(Name) -> 305 ImportMeta = import_meta(Meta, Name, 0, Q, E), 306 {'{}', [], [Name, meta(ImportMeta, Q), Q#elixir_quote.context]}; 307 308do_quote({Name, Meta, nil}, #elixir_quote{vars_hygiene=true} = Q, _E) when is_atom(Name) -> 309 {'{}', [], [Name, meta(Meta, Q), Q#elixir_quote.context]}; 310 311%% Unquote 312 313do_quote({{{'.', Meta, [Left, unquote]}, _, [Expr]}, _, Args}, #elixir_quote{unquote=true} = Q, E) -> 314 do_quote_call(Left, Meta, Expr, Args, Q, E); 315 316do_quote({{'.', Meta, [Left, unquote]}, _, [Expr]}, #elixir_quote{unquote=true} = Q, E) -> 317 do_quote_call(Left, Meta, Expr, nil, Q, E); 318 319%% Imports 320 321do_quote({'&', Meta, [{'/', _, [{F, _, C}, A]}] = Args}, 322 #elixir_quote{imports_hygiene=true} = Q, E) when is_atom(F), is_integer(A), is_atom(C) -> 323 do_quote_fa('&', Meta, Args, F, A, Q, E); 324 325do_quote({Name, Meta, Args}, #elixir_quote{imports_hygiene=true} = Q, E) when is_atom(Name), is_list(Args) -> 326 do_quote_import(Meta, Name, length(Args), Args, Q, E); 327 328do_quote({Name, Meta, Context}, #elixir_quote{imports_hygiene=true} = Q, E) when is_atom(Name), is_atom(Context) -> 329 do_quote_import(Meta, Name, 0, Context, Q, E); 330 331%% Two-element tuples 332 333do_quote({Left, Right}, #elixir_quote{unquote=true} = Q, E) when 334 is_tuple(Left) andalso (element(1, Left) == unquote_splicing); 335 is_tuple(Right) andalso (element(1, Right) == unquote_splicing) -> 336 do_quote({'{}', [], [Left, Right]}, Q, E); 337 338do_quote({Left, Right}, Q, E) -> 339 TLeft = do_quote(Left, Q, E), 340 TRight = do_quote(Right, Q, E), 341 {TLeft, TRight}; 342 343%% Everything else 344 345do_quote(Other, Q, E) when is_atom(E) -> 346 do_escape(Other, Q, E); 347 348do_quote({_, _, _} = Tuple, Q, E) -> 349 Annotated = annotate(Tuple, Q#elixir_quote.context), 350 do_quote_tuple(Annotated, Q, E); 351 352do_quote([], _, _) -> 353 []; 354 355do_quote([H | T], #elixir_quote{unquote=false} = Q, E) -> 356 do_quote_simple_list(T, do_quote(H, Q, E), Q, E); 357 358do_quote([H | T], Q, E) -> 359 do_quote_tail(lists:reverse(T, [H]), Q, E); 360 361do_quote(Other, _, _) -> 362 Other. 363 364%% do_escape 365 366do_escape({Left, Meta, Right}, Q, E = prune_metadata) -> 367 TM = [{K, V} || {K, V} <- Meta, (K == no_parens) orelse (K == line)], 368 TL = do_quote(Left, Q, E), 369 TR = do_quote(Right, Q, E), 370 {'{}', [], [TL, TM, TR]}; 371 372do_escape(Tuple, Q, E) when is_tuple(Tuple) -> 373 TT = do_quote(tuple_to_list(Tuple), Q, E), 374 {'{}', [], TT}; 375 376do_escape(BitString, _, _) when is_bitstring(BitString) -> 377 case bit_size(BitString) rem 8 of 378 0 -> 379 BitString; 380 Size -> 381 <<Bits:Size, Bytes/binary>> = BitString, 382 {'<<>>', [], [{'::', [], [Bits, {size, [], [Size]}]}, {'::', [], [Bytes, {binary, [], []}]}]} 383 end; 384 385do_escape(Map, Q, E) when is_map(Map) -> 386 TT = do_quote(maps:to_list(Map), Q, E), 387 {'%{}', [], TT}; 388 389do_escape([], _, _) -> []; 390 391do_escape([H | T], #elixir_quote{unquote=false} = Q, E) -> 392 do_quote_simple_list(T, do_quote(H, Q, E), Q, E); 393 394do_escape([H | T], Q, E) -> 395 %% The improper case is inefficient, but improper lists are rare. 396 try lists:reverse(T, [H]) of 397 L -> do_quote_tail(L, Q, E) 398 catch 399 _:_ -> 400 {L, R} = reverse_improper(T, [H]), 401 TL = do_quote_splice(L, Q, E, [], []), 402 TR = do_quote(R, Q, E), 403 update_last(TL, fun(X) -> {'|', [], [X, TR]} end) 404 end; 405 406do_escape(Other, _, _) 407 when is_number(Other); is_pid(Other); is_atom(Other) -> 408 Other; 409 410do_escape(Fun, _, _) when is_function(Fun) -> 411 case (erlang:fun_info(Fun, env) == {env, []}) andalso 412 (erlang:fun_info(Fun, type) == {type, external}) of 413 true -> fun_to_quoted(Fun); 414 false -> bad_escape(Fun) 415 end; 416 417do_escape(Other, _, _) -> 418 bad_escape(Other). 419 420bad_escape(Arg) -> 421 argument_error(<<"cannot escape ", ('Elixir.Kernel':inspect(Arg, []))/binary, ". ", 422 "The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, ", 423 "PIDs and remote functions in the format &Mod.fun/arity">>). 424 425import_meta(Meta, Name, Arity, Q, E) -> 426 case (keyfind(import, Meta) == false) andalso 427 elixir_dispatch:find_import(Meta, Name, Arity, E) of 428 false -> 429 case (Arity == 1) andalso keyfind(ambiguous_op, Meta) of 430 {ambiguous_op, nil} -> keystore(ambiguous_op, Meta, Q#elixir_quote.context); 431 _ -> Meta 432 end; 433 Receiver -> 434 keystore(import, keystore(context, Meta, Q#elixir_quote.context), Receiver) 435 end. 436 437%% do_quote_* 438 439do_quote_import(Meta, Name, Arity, ArgsOrAtom, Q, E) -> 440 ImportMeta = import_meta(Meta, Name, Arity, Q, E), 441 Annotated = annotate({Name, ImportMeta, ArgsOrAtom}, Q#elixir_quote.context), 442 do_quote_tuple(Annotated, Q, E). 443 444do_quote_call(Left, Meta, Expr, Args, Q, E) -> 445 All = [Left, {unquote, Meta, [Expr]}, Args, Q#elixir_quote.context], 446 TAll = [do_quote(X, Q, E) || X <- All], 447 {{'.', Meta, [elixir_quote, dot]}, Meta, [meta(Meta, Q) | TAll]}. 448 449do_quote_fa(Target, Meta, Args, F, A, Q, E) -> 450 NewMeta = 451 case elixir_dispatch:find_import(Meta, F, A, E) of 452 false -> Meta; 453 Receiver -> 454 lists:keystore(context, 1, 455 lists:keystore(import, 1, Meta, {import, Receiver}), 456 {context, Q#elixir_quote.context} 457 ) 458 end, 459 do_quote_tuple(Target, NewMeta, Args, Q, E). 460 461do_quote_tuple({Left, Meta, Right}, Q, E) -> 462 do_quote_tuple(Left, Meta, Right, Q, E). 463 464do_quote_tuple(Left, Meta, Right, Q, E) -> 465 TLeft = do_quote(Left, Q, E), 466 TRight = do_quote(Right, Q, E), 467 {'{}', [], [TLeft, meta(Meta, Q), TRight]}. 468 469do_quote_simple_list([], Prev, _, _) -> [Prev]; 470do_quote_simple_list([H | T], Prev, Q, E) -> 471 [Prev | do_quote_simple_list(T, do_quote(H, Q, E), Q, E)]; 472do_quote_simple_list(Other, Prev, Q, E) -> 473 [{'|', [], [Prev, do_quote(Other, Q, E)]}]. 474 475do_quote_tail([{'|', Meta, [{unquote_splicing, _, [Left]}, Right]} | T], #elixir_quote{unquote=true} = Q, E) -> 476 %% Process the remaining entries on the list. 477 %% For [1, 2, 3, unquote_splicing(arg) | tail], this will quote 478 %% 1, 2 and 3, which could even be unquotes. 479 TT = do_quote_splice(T, Q, E, [], []), 480 TR = do_quote(Right, Q, E), 481 do_runtime_list(Meta, tail_list, [Left, TR, TT]); 482 483do_quote_tail(List, Q, E) -> 484 do_quote_splice(List, Q, E, [], []). 485 486do_quote_splice([{unquote_splicing, Meta, [Expr]} | T], #elixir_quote{unquote=true} = Q, E, Buffer, Acc) -> 487 Runtime = do_runtime_list(Meta, list, [Expr, do_list_concat(Buffer, Acc)]), 488 do_quote_splice(T, Q, E, [], Runtime); 489 490do_quote_splice([H | T], Q, E, Buffer, Acc) -> 491 TH = do_quote(H, Q, E), 492 do_quote_splice(T, Q, E, [TH | Buffer], Acc); 493 494do_quote_splice([], _Q, _E, Buffer, Acc) -> 495 do_list_concat(Buffer, Acc). 496 497do_list_concat(Left, []) -> Left; 498do_list_concat([], Right) -> Right; 499do_list_concat(Left, Right) -> {{'.', [], [erlang, '++']}, [], [Left, Right]}. 500 501do_runtime_list(Meta, Fun, Args) -> 502 {{'.', Meta, [elixir_quote, Fun]}, Meta, Args}. 503 504%% Helpers 505 506meta(Meta, Q) -> 507 generated(keep(Meta, Q), Q). 508 509generated(Meta, #elixir_quote{generated=true}) -> [{generated, true} | Meta]; 510generated(Meta, #elixir_quote{generated=false}) -> Meta. 511 512keep(Meta, #elixir_quote{file=nil, line=Line}) -> 513 line(Meta, Line); 514keep(Meta, #elixir_quote{file=File}) -> 515 case lists:keytake(line, 1, Meta) of 516 {value, {line, Line}, MetaNoLine} -> 517 [{keep, {File, Line}} | MetaNoLine]; 518 false -> 519 [{keep, {File, 0}} | Meta] 520 end. 521 522line(Meta, true) -> 523 Meta; 524line(Meta, false) -> 525 keydelete(line, Meta); 526line(Meta, Line) -> 527 keystore(line, Meta, Line). 528 529reverse_improper([H | T], Acc) -> reverse_improper(T, [H | Acc]); 530reverse_improper([], Acc) -> Acc; 531reverse_improper(T, Acc) -> {Acc, T}. 532 533update_last([], _) -> []; 534update_last([H], F) -> [F(H)]; 535update_last([H | T], F) -> [H | update_last(T, F)]. 536 537keyfind(Key, Meta) -> 538 lists:keyfind(Key, 1, Meta). 539keydelete(Key, Meta) -> 540 lists:keydelete(Key, 1, Meta). 541keystore(_Key, Meta, nil) -> 542 Meta; 543keystore(Key, Meta, Value) -> 544 lists:keystore(Key, 1, Meta, {Key, Value}). 545keynew(Key, Meta, Value) -> 546 case lists:keymember(Key, 1, Meta) of 547 true -> Meta; 548 false -> [{Key, Value} | Meta] 549 end. 550