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; 015 016/** 017 * Represents a conditional branching. 018 * 019 * <p>Typically an {@code if else} node. 020 */ 021public final class ConditionalBranching extends GoloStatement<ConditionalBranching> { 022 023 private ExpressionStatement<?> condition; 024 private Block trueBlock; 025 private ConditionalBranching elseConditionalBranching; 026 private Block falseBlock; 027 028 private ConditionalBranching() { 029 super(); 030 } 031 032 /** 033 * Creates an empty conditional branch. 034 */ 035 public static ConditionalBranching branch() { 036 return new ConditionalBranching(); 037 } 038 039 /** 040 * Full branch construction in one call. 041 * 042 * <p>Less readable than the fluent API, but useful when doing meta-generation 043 * 044 * @param condition the test condition 045 * @param trueBlock the block to execute when the condition is true 046 * @param falseBlock the block to execute when the condition is false 047 * @param elseBranch a nested conditional branch 048 */ 049 public static ConditionalBranching create(Object condition, 050 Object trueBlock, 051 Object falseBlock, 052 Object elseBranch) { 053 return new ConditionalBranching().condition(condition) 054 .whenTrue(trueBlock) 055 .whenFalse(falseBlock) 056 .elseBranch(elseBranch); 057 } 058 059 protected ConditionalBranching self() { return this; } 060 061 /** 062 * Defines the condition of the branching. 063 * 064 * <p>This is a builder method. 065 * 066 * @param cond the expression defining the condition. If {@code null}, the condition is set to {@code false}. 067 * @return the branching. 068 */ 069 public ConditionalBranching condition(Object cond) { 070 if (cond == null) { 071 this.condition = ConstantStatement.of(false); 072 } else { 073 this.condition = ExpressionStatement.of(cond); 074 } 075 makeParentOf(this.condition); 076 return this; 077 } 078 079 /** 080 * Defines the block executed when the condition evaluates to {@code true}. 081 * 082 * <p>This is a builder method. 083 */ 084 public ConditionalBranching whenTrue(Object block) { 085 this.trueBlock = makeParentOf(Block.of(block)); 086 return this; 087 } 088 089 /** 090 * Defines the block executed when the condition evaluates to {@code false}. 091 * 092 * <p>This is a builder method. 093 * 094 * @see #otherwise(Object) 095 */ 096 public ConditionalBranching whenFalse(Object block) { 097 if (block == null) { 098 this.falseBlock = null; 099 } else { 100 this.falseBlock = makeParentOf(Block.of(block)); 101 } 102 return this; 103 } 104 105 /** 106 * Defines a nested conditional branch. 107 * 108 * <p>This is a builder method. 109 * 110 * <p>This represents an {@code else if} branch. 111 * 112 * @see #otherwise(Object) 113 */ 114 public ConditionalBranching elseBranch(Object elseBranch) { 115 this.elseConditionalBranching = makeParentOf((ConditionalBranching) elseBranch); 116 return this; 117 } 118 119 /** 120 * Defines a block to execute when {@code false} or a nested branch according to the given element. 121 * 122 * <p>This is a builder method; it's the preferred way to define an alternative to the true block. 123 * 124 * @param alternative a {@link Block} to execute when {@code false} or a {@code ConditionalBranching} defining a 125 * nested branch. 126 */ 127 public ConditionalBranching otherwise(Object alternative) { 128 if (alternative instanceof ConditionalBranching) { 129 return elseBranch(alternative); 130 } 131 return whenFalse(alternative); 132 } 133 134 public ExpressionStatement<?> getCondition() { 135 return condition; 136 } 137 138 public Block getTrueBlock() { 139 return trueBlock; 140 } 141 142 public Block getFalseBlock() { 143 return falseBlock; 144 } 145 146 public boolean hasFalseBlock() { 147 return falseBlock != null; 148 } 149 150 public ConditionalBranching getElseConditionalBranching() { 151 return elseConditionalBranching; 152 } 153 154 public boolean hasElseConditionalBranching() { 155 return elseConditionalBranching != null; 156 } 157 158 public boolean returnsFromBothBranches() { 159 if (hasFalseBlock()) { 160 return trueBlock.hasReturn() && falseBlock.hasReturn(); 161 } else if (hasElseConditionalBranching()) { 162 return trueBlock.hasReturn() && elseConditionalBranching.returnsFromBothBranches(); 163 } else { 164 return false; 165 } 166 } 167 168 @Override 169 public String toString() { 170 return String.format("if %s %s%s", condition, trueBlock, 171 hasFalseBlock() ? " else " + falseBlock.toString() 172 : hasElseConditionalBranching() ? " else " + elseConditionalBranching.toString() 173 : ""); 174 } 175 176 /** 177 * {@inheritDoc} 178 */ 179 @Override 180 public void accept(GoloIrVisitor visitor) { 181 visitor.visitConditionalBranching(this); 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override 188 public List<GoloElement<?>> children() { 189 LinkedList<GoloElement<?>> children = new LinkedList<>(); 190 children.add(condition); 191 children.add(trueBlock); 192 if (falseBlock != null) { 193 children.add(falseBlock); 194 } 195 if (elseConditionalBranching != null) { 196 children.add(elseConditionalBranching); 197 } 198 return children; 199 } 200 201 /** 202 * {@inheritDoc} 203 */ 204 @Override 205 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 206 if (condition == original) { 207 condition(newElement); 208 } else if (trueBlock == original) { 209 whenTrue(newElement); 210 } else if (elseConditionalBranching == original && newElement instanceof ConditionalBranching) { 211 elseBranch(newElement); 212 } else if (elseConditionalBranching == original) { 213 whenFalse(newElement instanceof Noop ? null : newElement); 214 elseBranch(null); 215 } else if (falseBlock == original && newElement instanceof ConditionalBranching) { 216 elseBranch(newElement); 217 whenFalse(null); 218 } else if (falseBlock == original) { 219 whenFalse(newElement instanceof Noop ? null : newElement); 220 } else { 221 throw cantReplace(original, newElement); 222 } 223 } 224 225}