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.lang.reflect.Method;
014
015/**
016 * Represents a function call.
017 */
018public final class FunctionInvocation extends AbstractInvocation<FunctionInvocation> {
019
020  private boolean onReference = false;
021  private boolean onModuleState = false;
022  private boolean anonymous = false;
023  private boolean constant = false;
024
025  private FunctionInvocation() {
026    super("anonymous");
027    anonymous = true;
028  }
029
030  private FunctionInvocation(String name) {
031    super(name);
032  }
033
034  /**
035   * Full function invocation creation in one call.
036   * <p>
037   * A lot less readable than the fluent API, but useful when doing meta-generation
038   *
039   * @param name the name of the function to call
040   * @param onReference tells if the call is on a reference
041   * @param onModuleState tells if the call in on a module level variable
042   * @param constant tells if the call is constant (banged)
043   * @param args the call arguments
044   * @return the fully built corresponding invocation
045   * @see #of(Object)
046   */
047  public static FunctionInvocation create(String name, boolean onReference, boolean onModuleState, boolean constant, Object... args) {
048    return of(name)
049      .onReference(onReference)
050      .onModuleState(onModuleState)
051      .constant(constant)
052      .withArgs(args);
053  }
054
055
056  /**
057   * Calls a function by name.
058   *
059   * <p>Typical usage:
060   * <pre class="listing"><code class="lang-java" data-lang="java">
061   * FunctionInvocation.of("foo").withArgs(constant("answer "), constant(42))
062   * </code></pre>
063   * creates
064   * <pre class="listing"><code class="lang-golo" data-lang="golo">
065   * foo("answer ", 42)
066   * </code></pre>
067   *
068   * @param name the name of the function to call, a {@link GoloFunction}, a {@link ReferenceLookup} or a {@code java.lang.reflect.Method}
069   * @return the corresponding invocation
070   */
071  public static FunctionInvocation of(Object name) {
072    if (name == null || "".equals(name)) {
073      return new FunctionInvocation();
074    }
075    if (name instanceof FunctionInvocation) {
076      return (FunctionInvocation) name;
077    }
078    if (name instanceof GoloFunction) {
079      return new FunctionInvocation(((GoloFunction) name).getName());
080    }
081    if (name instanceof Method) {
082      Method m = (Method) name;
083      return new FunctionInvocation(m.getDeclaringClass().getCanonicalName() + "." + m.getName());
084    }
085    if (name instanceof ReferenceLookup) {
086      return new FunctionInvocation(((ReferenceLookup) name).getName()).onReference(true);
087    }
088    return new FunctionInvocation(name.toString());
089  }
090
091  protected FunctionInvocation self() { return this; }
092
093  /**
094   * Define this call as being on a reference or not.
095   *
096   * <p>For instance in:
097   * <pre class="listing"><code class="lang-golo" data-lang="golo">
098   * let f = |x| -> x + 2
099   * f(40)
100   * </code></pre>
101   * the call to {@code f} is on a reference.
102   */
103  public FunctionInvocation onReference(boolean isOnReference) {
104    this.onReference = isOnReference;
105    return this;
106  }
107
108  /**
109   * Define this call as being on a reference.
110   *
111   * Same as {@code onReference(true)}.
112   */
113  public FunctionInvocation onReference() {
114    return onReference(true);
115  }
116
117  public boolean isOnReference() {
118    return onReference;
119  }
120
121  /**
122   * Checks if this call is anonymous.
123   *
124   * <p>For instance, in:
125   * <pre class="listing"><code class="lang-golo" data-lang="golo">
126   * f("answer")(42)
127   * </code></pre>
128   * the call with {@code 42} is an anonymous one.
129   */
130  public boolean isAnonymous() {
131    return anonymous;
132  }
133
134  public FunctionInvocation onModuleState(boolean isOnModuleState) {
135    this.onModuleState = isOnModuleState;
136    return this;
137  }
138
139  public FunctionInvocation onModuleState() {
140    return onModuleState(true);
141  }
142
143  public boolean isOnModuleState() {
144    return onModuleState;
145  }
146
147  public FunctionInvocation constant(boolean isConstant) {
148    this.constant = isConstant;
149    return this;
150  }
151
152  public FunctionInvocation constant() {
153    return this.constant(true);
154  }
155
156  public boolean isConstant() {
157    return constant;
158  }
159
160  protected FunctionInvocation copy() {
161    return create(anonymous ? null : getName(),
162        onReference, onModuleState, constant, getArguments().toArray());
163  }
164
165  /**
166   * {@inheritDoc}
167   */
168  @Override
169  public FunctionInvocation withArgs(Object... arguments) {
170    super.withArgs(arguments);
171    return this;
172  }
173
174  /**
175   * {@inheritDoc}
176   */
177  @Override
178  public String toString() {
179    return String.format("FunctionInvocation{name=%s, arity=%s}", getName(), getArity());
180  }
181
182  /**
183   * {@inheritDoc}
184   */
185  @Override
186  public void accept(GoloIrVisitor visitor) {
187    visitor.visitFunctionInvocation(this);
188  }
189}