001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang.ir;
012
013import java.util.LinkedList;
014
015public abstract class ExpressionStatement<T extends ExpressionStatement<T>> extends GoloStatement<T> {
016
017  private final LinkedList<GoloAssignment<?>> declarations = new LinkedList<>();
018
019  /**
020   * Defines a variable declaration (assignment) local to this expression.
021   */
022  public T with(Object a) {
023    if (!(a instanceof GoloAssignment)) {
024      throw new IllegalArgumentException(("Must be an assignment, got " + a));
025    }
026    GoloAssignment<?> declaration = (GoloAssignment<?>) a;
027    declarations.add(declaration.declaring());
028    makeParentOf(declaration);
029    return self();
030  }
031
032  /**
033   * Returns the local declarations of this expression if any.
034   */
035  public GoloAssignment<?>[] declarations() {
036    return declarations.toArray(new GoloAssignment<?>[declarations.size()]);
037  }
038
039  /**
040   * Checks if this expression has local variable declarations.
041   */
042  public boolean hasLocalDeclarations() {
043    return !declarations.isEmpty();
044  }
045
046  /**
047   * Removes all local declarations.
048   */
049  public void clearDeclarations() {
050    declarations.clear();
051  }
052
053  /**
054   * Creates an binary operation representing the anonymous call on this expression.
055   *
056   * <p>For instance, code such as:
057   * <pre class="listing"><code class="lang-golo" data-lang="golo">
058   * (|x| -> x + 2)(40)
059   * </code></pre>
060   * actually creates a {@link BinaryOperation} or type {@link OperatorType#ANON_CALL}
061   * whose first argument is a {@code ClosureReference} and the second one a {@link FunctionInvocation} holding the
062   * argument.
063   *
064   * <p>This method ease the creation of such a node.
065   * This expression must return a {@link gololang.FunctionReference} when evaluated. This is <em>not checked</em>.
066   *
067   * <p>For instance, in code like
068   * <pre class="listing"><code class="lang-golo" data-lang="golo">
069   * let f = |x| -> |y| -> x * y
070   * f(2)(42)
071   * </code></pre>
072   * the call is an anonymous call between {@code f(2)} and {@code (42)}, and can be generated by:
073   * <pre class="listing"><code class="lang-java" data-lang="java">
074   * call("f").withArgs(constant(2)).call(constant(42))
075   * </pre></code>
076   * <p>See also the
077   * <a href="http://golo-lang.org/documentation/next/#_calling_functions_that_return_functions">
078   * Golo guide</a>.
079   */
080  public BinaryOperation call(Object... arguments) {
081    FunctionInvocation invocation;
082    if (arguments.length == 1 && arguments[0] instanceof FunctionInvocation) {
083      invocation = (FunctionInvocation) arguments[0];
084      if (!invocation.isAnonymous()) {
085        throw new IllegalArgumentException("Invocation in anonymous calls must be anonymous.");
086      }
087    } else {
088      invocation = FunctionInvocation.of(null).withArgs(arguments);
089    }
090    return BinaryOperation.create(OperatorType.ANON_CALL, this, invocation);
091  }
092
093  /**
094   * Expression coercion.
095   *
096   * <p>If the given value is an expression, casts it. If it's a literal value, returns a {@code ConstantStatement}.
097   * If it's a {@link LocalReference}, creates a {@link ReferenceLookup} from it.
098   *
099   * @see ConstantStatement#isLiteralValue(Object)
100   */
101  public static ExpressionStatement<?> of(Object expr) {
102    if (expr instanceof ExpressionStatement) {
103      return (ExpressionStatement<?>) expr;
104    }
105    if (ConstantStatement.isLiteralValue(expr)) {
106      return ConstantStatement.of(expr);
107    }
108    if (expr instanceof LocalReference) {
109      return ((LocalReference) expr).lookup();
110    }
111    throw cantConvert("ExpressionStatement", expr);
112  }
113}