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 gololang.FunctionReference; 014 015import java.util.ArrayList; 016import java.util.Iterator; 017import java.util.LinkedHashSet; 018import java.util.List; 019 020/** 021 * A container of top-level {@code GoloElement}. 022 * <p> 023 * This class is mainly used by top-level macros to return a collection of golo top-level elements, 024 * i.e. functions, structs, augments and so on, since a macro must return a unique GoloElement to 025 * inject in the Ir by replacing the macro call. 026 * 027 * <p>This element must never be present in a tree. 028 */ 029public final class ToplevelElements extends GoloElement<ToplevelElements> implements Iterable<GoloElement<?>> { 030 031 private final LinkedHashSet<GoloElement<?>> elements = new LinkedHashSet<>(); 032 033 private ToplevelElements(GoloElement<?>... elements) { 034 for (GoloElement<?> e : elements) { 035 this.add(e); 036 } 037 } 038 039 /** 040 * Creates a top-level elements container. 041 * 042 * <p>Mainly used to return several nodes from a top-level macro. 043 * <p>If only a {@code ToplevelElements} instance is given, it is returned unchanged. 044 * 045 * @param elements the Golo Elements to add. 046 * @return a {@code ToplevelElements} containing all the given elements. 047 */ 048 public static ToplevelElements of(Object... elements) { 049 if (elements.length == 1 && elements[0] instanceof ToplevelElements) { 050 return (ToplevelElements) elements[0]; 051 } 052 if (elements.length == 1 && elements[0] instanceof Iterable) { 053 @SuppressWarnings("unchecked") 054 Iterable<Object> it = (Iterable<Object>) elements[0]; 055 return fromIterable(it); 056 } 057 ToplevelElements tl = new ToplevelElements(); 058 for (Object e : elements) { 059 tl.add(e); 060 } 061 return tl; 062 } 063 064/** 065 * Creates a top-level elements container. 066 * 067 * <p>Mainly used to return several nodes from a top-level macro. 068 */ 069 public static ToplevelElements fromIterable(Iterable<Object> elements) { 070 ToplevelElements tl = new ToplevelElements(); 071 for (Object e : elements) { 072 tl.add(e); 073 } 074 return tl; 075 } 076 077 protected ToplevelElements self() { return this; } 078 079 public ToplevelElements add(Object element) { 080 if (element instanceof ToplevelElements) { 081 for (GoloElement<?> e : (ToplevelElements) element) { 082 this.add(e); 083 } 084 } else if (element instanceof ToplevelGoloElement && !(element instanceof Noop)) { 085 GoloElement<?> elt = (GoloElement<?>) element; 086 this.elements.add(makeParentOf(elt)); 087 } else { 088 throw new IllegalArgumentException(element.toString()); 089 } 090 return this; 091 } 092 093 public boolean isEmpty() { 094 return this.elements.isEmpty(); 095 } 096 097 /** 098 * Map a golo function on the contained elements. 099 * <p> 100 * This can be used in top-level macros to apply the macro on a top-level from a previous macro application, for 101 * instance when stacking decorator-like macros returning a {@code ToplevelElements}. 102 */ 103 public ToplevelElements map(FunctionReference fun) throws Throwable { 104 ToplevelElements res = new ToplevelElements(); 105 for (GoloElement<?> elt : this) { 106 res.add(fun.invoke(elt)); 107 } 108 return res; 109 } 110 111 @Override 112 public Iterator<GoloElement<?>> iterator() { 113 return elements.iterator(); 114 } 115 116 @Override 117 public List<GoloElement<?>> children() { 118 return new ArrayList<>(elements); 119 } 120 121 @Override 122 public void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 123 if (elements.contains(original)) { 124 elements.remove(original); 125 add(newElement); 126 } else { 127 throw cantReplace(original, newElement); 128 } 129 } 130 131 @Override 132 public void accept(GoloIrVisitor visitor) { 133 visitor.visitToplevelElements(this); 134 } 135} 136