package org.elixir_lang; import com.ericsson.otp.erlang.*; import com.google.common.base.Joiner; import org.elixir_lang.psi.impl.ElixirPsiImplUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; public class Macro { public static OtpErlangList callArguments(OtpErlangTuple callExpression) { return (OtpErlangList) callExpression.elementAt(2); } public static boolean isAliases(OtpErlangObject macro) { boolean aliases = false; if (isExpression(macro)) { OtpErlangTuple expression = (OtpErlangTuple) macro; OtpErlangObject first = expression.elementAt(0); if (first instanceof OtpErlangAtom) { OtpErlangAtom firstAtom = (OtpErlangAtom) first; if (firstAtom.atomValue().equals("__aliases__")) { aliases = true; } } } return aliases; } /** * Return whether quoted form contains an Elixir keyword that is just an alias to an atom, such as `false`, * `true`, or `nil`. * * @param macro a quoted form from a {@code quote} method. * @return {@code true} if OtpErlangAtom containing one of the keywords. */ public static boolean isAtomKeyword(OtpErlangObject macro) { boolean atomKeyword = false; for (OtpErlangAtom knownAtomKeyword : ElixirPsiImplUtil.ATOM_KEYWORDS) { if (macro.equals(knownAtomKeyword)) { atomKeyword = true; break; } } return atomKeyword; } /** * Return whether the macro is a __block__ expression representing sequential lines. * * @param macro a quoted form from a {@code quote} method. * @return {@code true} if `{:__block__, _, _}`. */ public static boolean isBlock(OtpErlangObject macro) { boolean block = false; if (isExpression(macro)) { OtpErlangTuple expression = (OtpErlangTuple) macro; if (expression.elementAt(0).equals(ElixirPsiImplUtil.BLOCK)) { block = true; } } return block; } /** * Return whether the macro is an Expr node: `expr :: {expr | atom, Keyword.t, atom | [t]}`. * * @param macro a quoted form from a {@code quote} method. * @return {@code true} if a tuple with 3 elements; {@code false} otherwise. */ public static boolean isExpression(OtpErlangObject macro) { boolean expression = false; if (macro instanceof OtpErlangTuple) { OtpErlangTuple tuple = (OtpErlangTuple) macro; if (tuple.arity() == 3) { expression = true; } } return expression; } /** * Returns whether macro is a local call. * * @param macro a quoted form * @return {@code true} if local call; {@code false} otherwise. * @see <a href="https://github.com/elixir-lang/elixir/blob/6151f2ab1af0189b9c8c526db196e2a65c609c64/lib/elixir/lib/macro.ex#L277-L281">Macro.decompose_call/1</a> */ public static boolean isLocalCall(OtpErlangObject macro) { boolean localCall = false; if (isExpression(macro)) { OtpErlangTuple expression = (OtpErlangTuple) macro; OtpErlangObject first = expression.elementAt(0); if (first instanceof OtpErlangAtom) { OtpErlangObject last = expression.elementAt(2); /* OtpErlangString maps to CharList, which are list, so is_list in Elixir would be true for OtpErlangList and OtpErlangString. */ if (last instanceof OtpErlangList || last instanceof OtpErlangString) { localCall = true; } } } return localCall; } /** Return whether macro is a local call expression with no arguments. * * @param macro * @return */ public static boolean isVariable(OtpErlangObject macro) { boolean variable = false; if (isExpression(macro)) { OtpErlangTuple expression = (OtpErlangTuple) macro; OtpErlangObject first = expression.elementAt(0); if (first instanceof OtpErlangAtom) { OtpErlangObject last = expression.elementAt(2); if (last instanceof OtpErlangAtom) { variable = true; } } } return variable; } public static OtpErlangList metadata(OtpErlangTuple expression) { return (OtpErlangList) expression.elementAt(1); } /** * * @param macro * @return * @see <a href="https://github.com/elixir-lang/elixir/blob/a1b06e1e9067ae08826f91274fefcb68c2bbdd02/lib/elixir/lib/macro.ex#L461-L464">Macro.to_string</a> */ @NotNull public static String toString(OtpErlangObject macro) { assert Macro.isAliases(macro); OtpErlangTuple quotedAlias = (OtpErlangTuple) macro; OtpErlangList refs = (OtpErlangList) quotedAlias.elementAt(2); java.util.List<String> refStringList = new ArrayList<String>(); for (OtpErlangObject ref : refs) { OtpErlangAtom refAtom = (OtpErlangAtom) ref; refStringList.add(refAtom.atomValue()); } return Joiner.on(".").join(refStringList); } }