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}