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.List; 014import java.util.LinkedList; 015import java.util.Objects; 016 017/** 018 * Represents a {@code foreach} loop on an iterator. 019 * <p>For instance: 020 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 021 * foreach x in range(5) { 022 * println(x) 023 * } 024 * </code></pre> 025 */ 026public final class ForEachLoopStatement extends GoloStatement<ForEachLoopStatement> implements BlockContainer<ForEachLoopStatement>, ReferencesHolder { 027 private Block block = Block.empty(); 028 private ExpressionStatement<?> iterable; 029 private final List<LocalReference> valueRefs = new LinkedList<>(); 030 private ExpressionStatement<?> whenClause; 031 private boolean isVarargs = false; 032 033 private ForEachLoopStatement() { 034 super(); 035 } 036 037 /** 038 * Complete foreach loop. 039 * 040 * For meta-generation. 041 * 042 * @param varargs the parameters of the loop are variable length destructuring 043 * @param iterable the iterable we loop on 044 * @param when the loop filter 045 * @param block the loop block 046 * @param vars the loop variables 047 */ 048 public static ForEachLoopStatement create(boolean varargs, Object iterable, Object when, Object block, Object... vars) { 049 ForEachLoopStatement loop = new ForEachLoopStatement(); 050 for (Object v : vars) { 051 loop.var(v); 052 } 053 return loop.varargs(varargs).when(when).in(iterable).block(block); 054 } 055 056 public static ForEachLoopStatement create() { 057 return new ForEachLoopStatement(); 058 } 059 060 /** 061 * {@inheritDoc} 062 */ 063 @Override 064 public ForEachLoopStatement block(Object block) { 065 this.block = makeParentOf(Block.of(block)); 066 return this; 067 } 068 069 protected ForEachLoopStatement self() { return this; } 070 071 /** 072 * Defines the iterable on which to loop. 073 * 074 * <p>This is a builder method. 075 * 076 * @param iterable an {@link ExpressionStatement} representing an {@code Iterable} expression. 077 */ 078 public ForEachLoopStatement in(Object iterable) { 079 this.iterable = ExpressionStatement.of(iterable); 080 return this; 081 } 082 083 public ForEachLoopStatement varargs(boolean b) { 084 this.isVarargs = b; 085 return this; 086 } 087 088 /** 089 * Adds a loop variable. 090 * 091 * <p>This is a builder method. 092 * 093 * @param varRef the variable (a {@link LocalReference} create) 094 * @see LocalReference#of(Object) 095 */ 096 public ForEachLoopStatement var(Object varRef) { 097 this.valueRefs.add(LocalReference.of(varRef).variable()); 098 return this; 099 } 100 101 /** 102 * Adds a when clause to the loop. 103 * 104 * <p>This is a builder method. 105 * 106 * @param clause an {@link ExpressionStatement} used as a condition. 107 */ 108 public ForEachLoopStatement when(Object clause) { 109 if (clause != null) { 110 this.whenClause = ExpressionStatement.of(clause); 111 } else { 112 this.whenClause = null; 113 } 114 return this; 115 } 116 117 public ExpressionStatement<?> getIterable() { 118 return iterable; 119 } 120 121 public Block getBlock() { 122 return block; 123 } 124 125 public boolean isDestructuring() { 126 return valueRefs.size() > 1; 127 } 128 129 public boolean isVarargs() { 130 return this.isVarargs; 131 } 132 133 public LocalReference getLocalReference() { 134 return valueRefs.get(0); 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public LocalReference[] getReferences() { 142 return valueRefs.toArray(new LocalReference[valueRefs.size()]); 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 public int getReferencesCount() { 150 return valueRefs.size(); 151 } 152 153 public boolean hasWhenClause() { 154 return whenClause != null; 155 } 156 157 public ExpressionStatement<?> getWhenClause() { 158 return whenClause; 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 public void accept(GoloIrVisitor visitor) { 166 visitor.visitForEachLoopStatement(this); 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override 173 public List<GoloElement<?>> children() { 174 LinkedList<GoloElement<?>> children = new LinkedList<>(valueRefs); 175 children.add(iterable); 176 if (whenClause != null) { 177 children.add(whenClause); 178 } 179 children.add(block); 180 return children; 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 @Override 187 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 188 if (Objects.equals(iterable, original)) { 189 in(newElement); 190 } else if (Objects.equals(whenClause, original)) { 191 when(newElement); 192 } else if (Objects.equals(block, original)) { 193 block(newElement); 194 } else { 195 throw cantReplace(original, newElement); 196 } 197 } 198}