001/*
002 * Copyright (c) 2012-2021 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.Collections;
014import java.util.LinkedList;
015import java.util.List;
016
017import org.eclipse.golo.compiler.PackageAndClass;
018
019/**
020 * Abstract representation of a Golo invocation in the IR tree.
021 * <p>
022 * Used to represent regular function call, method invocation, and macro call.
023 * <p>
024 * Note that the called object is only refereed by its name and not an IR node, since the linkage is done at runtime.
025 * There is no guaranty that such an object exists.
026 */
027public abstract class AbstractInvocation<T extends AbstractInvocation<T>> extends ExpressionStatement<T> {
028
029  private PackageAndClass packageAndClass;
030  private final List<GoloElement<?>> arguments = new LinkedList<>();
031  protected boolean usesNamedArguments = false;
032
033  AbstractInvocation(String name) {
034    super();
035    this.packageAndClass = PackageAndClass.of(name);
036  }
037
038  /**
039   * Returns the fully qualified name of the called object.
040   */
041  public String getName() {
042    return packageAndClass.toString();
043  }
044
045  /**
046   * Returns the module part of the called object name.
047   */
048  public String getModuleName() {
049    if (packageAndClass == null) { return ""; }
050    return packageAndClass.packageName();
051  }
052
053  /**
054   * Returns the object part of the called object name.
055   */
056  public String getFunctionName() {
057    if (packageAndClass == null) { return ""; }
058    return packageAndClass.className();
059  }
060
061  public void setPackageAndClass(PackageAndClass name) {
062    this.packageAndClass = name;
063  }
064
065  public PackageAndClass getPackageAndClass() {
066    return this.packageAndClass;
067  }
068
069  private void addArgument(GoloElement<?> argument) {
070    arguments.add(makeParentOf(argument));
071    if (argument instanceof NamedArgument) {
072      withNamedArguments();
073    }
074  }
075
076  /**
077   * Defines the values of the arguments for this invocation.
078   *
079   * <p>This is a builder method.
080   * <p>This methods <em>appends</em> the arguments to the existing ones, so that it can be called multiple times to
081   * create the invocation incrementally. On the other hand, it is <em>not</em> idempotent.
082   * <p>Calls {@link #withNamedArguments()} if needed.
083   *
084   * @param arguments the arguments of the invocation. An argument can be any {@link GoloElement}, or are converted with
085   * {@link ExpressionStatement#of(Object)} otherwise, so that strings and primitives are automatically wrapped in a
086   * {@link ConstantStatement}.
087   * @return this invocation
088   * @see #withNamedArguments()
089   * @see ExpressionStatement#of(Object)
090   */
091  // NOTE: arguments are GoloElement and not ExpressionStatement since macro calls can be applied to top-level elements
092  // (e.g. struct or function definitions) that are not expressions.
093  public T withArgs(Object... arguments) {
094    for (Object argument : arguments) {
095      if (argument instanceof GoloElement) {
096        addArgument((GoloElement) argument);
097      } else {
098        addArgument(ExpressionStatement.of(argument));
099      }
100    }
101    return self();
102  }
103
104  public List<GoloElement<?>> getArguments() {
105    return Collections.unmodifiableList(arguments);
106  }
107
108  /**
109   * Returns the number or arguments of this invocation.
110   *
111   * <p>It can be different of called object number of parameters if it is a vararg one.
112   * <p>Since the call is resolved at runtime, there is no guaranty that these two numbers match.
113   */
114  public int getArity() {
115    return arguments.size();
116  }
117
118  /**
119   * Checks if this call uses the named arguments syntax.
120   * <p>See the
121   * <a href="http://golo-lang.org/documentation/next/index.html#_named_parameters">Golo Guide on Named Parameters</a>
122   */
123  public boolean usesNamedArguments() {
124    return usesNamedArguments;
125  }
126
127  public boolean namedArgumentsComplete() {
128    return this.arguments.isEmpty() || this.usesNamedArguments;
129  }
130
131  /**
132   * Mark the invocation as using names arguments syntax.
133   *
134   * <p>This is a builder method.
135   *
136   * <p>This method should not need to be called directly since the {@link #withArgs(Object...)} one checks its argument
137   * and call this one if needed.
138   *
139   * @see #withArgs(Object...)
140   */
141  public T withNamedArguments() {
142    this.usesNamedArguments = true;
143    return self();
144  }
145
146  /**
147   * {@inheritDoc}
148   */
149  @Override
150  public List<GoloElement<?>> children() {
151    return Collections.unmodifiableList(arguments);
152  }
153
154  /**
155   * {@inheritDoc}
156   */
157  @Override
158  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
159    if (arguments.contains(original)) {
160      this.arguments.set(arguments.indexOf(original), newElement);
161      makeParentOf(newElement);
162      if (newElement instanceof NamedArgument) {
163        withNamedArguments();
164      }
165    } else {
166      throw cantReplace(original, newElement);
167    }
168  }
169}