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.Objects; 014import java.util.LinkedList; 015import java.util.List; 016 017/** 018 * Generic looping structure. 019 * 020 * <p>This is used to represent both {@code for} and {@code while} loops, as well as the desugared {@code foreach} loop. 021 */ 022public final class LoopStatement extends GoloStatement<LoopStatement> implements BlockContainer<LoopStatement>, ReferencesHolder { 023 024 private AssignmentStatement initStatement = null; 025 private ExpressionStatement<?> conditionStatement = null; 026 private GoloStatement<?> postStatement = null; 027 private Block block = null; 028 029 private LoopStatement() { 030 super(); 031 } 032 033 /** 034 * Creates a generic loop. 035 */ 036 public static LoopStatement loop() { 037 return new LoopStatement().condition(null).block(null); 038 } 039 040 /** 041 * Complete loop creation. 042 * 043 * For meta-generation. 044 */ 045 public static LoopStatement create(Object init, Object condition, Object post, Object block) { 046 return loop().init(init).condition(condition).post(post).block(Block.of(block)); 047 } 048 049 050 protected LoopStatement self() { return this; } 051 052 /** 053 * Defines the initialization of the loop variable. 054 * 055 * <p>For instance, the <code class="lang-golo">var i = 0</code> statement. 056 * This can be {@code null} in the case of {@code while} loops. 057 * 058 * <p>This is a builder method. 059 * 060 * @param assignment a {@link AssignmentStatement} defining the loop variable. 061 */ 062 public LoopStatement init(Object assignment) { 063 if (assignment instanceof AssignmentStatement) { 064 this.initStatement = makeParentOf((AssignmentStatement) assignment); 065 return this; 066 } 067 if (assignment == null) { 068 this.initStatement = null; 069 return this; 070 } 071 throw cantConvert("assignment", assignment); 072 } 073 074 /** 075 * Defines the loop condition. 076 * 077 * <p>For instance, the <code class="lang-golo">i < 10</code> expression. 078 * 079 * <p>This is a builder method. 080 * 081 * @param expression a {@link ExpressionStatement} evaluating to a boolean. 082 */ 083 public LoopStatement condition(Object expression) { 084 if (expression == null) { 085 this.conditionStatement = ConstantStatement.of(false); 086 } else if (expression instanceof ExpressionStatement) { 087 this.conditionStatement = ExpressionStatement.of(expression); 088 } else { 089 throw cantConvert("expression", expression); 090 } 091 makeParentOf(this.conditionStatement); 092 return this; 093 } 094 095 /** 096 * Define the post-loop statement. 097 * 098 * <p>For instance, the <code class="lang-golo">i = i + 1</code> statement. 099 * This can be {@code null} in the case of {@code while} loops. 100 * 101 * <p>This is a builder method. 102 * 103 * @param statement a {@link GoloStatement} changing the loop variable. 104 */ 105 public LoopStatement post(Object statement) { 106 if (statement instanceof GoloStatement) { 107 this.postStatement = makeParentOf((GoloStatement) statement); 108 return this; 109 } 110 if (statement == null) { 111 this.postStatement = null; 112 return this; 113 } 114 throw cantConvert("statement", statement); 115 } 116 117 /** 118 * {@inheritDoc} 119 */ 120 @Override 121 public LoopStatement block(Object innerBlock) { 122 this.block = makeParentOf(Block.of(innerBlock)); 123 return this; 124 } 125 126 public boolean hasInitStatement() { 127 return initStatement != null; 128 } 129 130 public AssignmentStatement init() { 131 return initStatement; 132 } 133 134 public ExpressionStatement<?> condition() { 135 return conditionStatement; 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 public Block getBlock() { 143 return block; 144 } 145 146 public GoloStatement<?> post() { 147 return postStatement; 148 } 149 150 public boolean hasPostStatement() { 151 return postStatement != null; 152 } 153 154 /** 155 * {@inheritDoc} 156 */ 157 @Override 158 public LocalReference[] getReferences() { 159 if (hasInitStatement()) { 160 return new LocalReference[]{init().getLocalReference()}; 161 } 162 return new LocalReference[0]; 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 public int getReferencesCount() { 170 return hasInitStatement() ? 1 : 0; 171 } 172 173 /** 174 * {@inheritDoc} 175 */ 176 @Override 177 public void accept(GoloIrVisitor visitor) { 178 visitor.visitLoopStatement(this); 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public List<GoloElement<?>> children() { 186 LinkedList<GoloElement<?>> children = new LinkedList<>(); 187 if (initStatement != null) { 188 children.add(initStatement); 189 } 190 if (conditionStatement != null) { 191 children.add(conditionStatement); 192 } 193 if (postStatement != null) { 194 children.add(postStatement); 195 } 196 if (block != null) { 197 children.add(block); 198 } 199 return children; 200 } 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override 206 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 207 if (Objects.equals(initStatement, original)) { 208 init(newElement); 209 } else if (Objects.equals(conditionStatement, original)) { 210 condition(newElement); 211 } else if (Objects.equals(postStatement, original)) { 212 post(newElement); 213 } else if (Objects.equals(block, original)) { 214 block(Block.of(newElement)); 215 } else { 216 throw cantReplace(original, newElement); 217 } 218 } 219}