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 016import static java.util.Collections.unmodifiableList; 017 018/** 019 * Represents a collection comprehension expression. 020 * 021 * <p>Such as: 022 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 023 * list[2*x + y foreach x in someIterable for (var y=0, y < 5, y = y + 1)] 024 * </code></pre> 025 * 026 * @see CollectionLiteral.Type 027 */ 028public final class CollectionComprehension extends ExpressionStatement<CollectionComprehension> { 029 030 private final CollectionLiteral.Type type; 031 private ExpressionStatement<?> expression; 032 private final List<GoloStatement<?>> loopBlocks = new LinkedList<>(); 033 034 private CollectionComprehension(CollectionLiteral.Type type) { 035 super(); 036 this.type = type; 037 } 038 039 /** 040 * Creates a collection comprehension of the given type. 041 */ 042 public static CollectionComprehension of(Object type) { 043 return new CollectionComprehension( 044 (type instanceof CollectionLiteral.Type) 045 ? (CollectionLiteral.Type) type 046 : CollectionLiteral.Type.valueOf(type.toString())); 047 } 048 049 /** 050 * Complete collection comprehension creation. 051 * 052 * For meta-generation. 053 */ 054 public static CollectionComprehension create(Object type, Object expression, Object... loops) { 055 CollectionComprehension c = of(type).expression(expression); 056 for (Object l : loops) { 057 c.loop(l); 058 } 059 return c; 060 } 061 062 protected CollectionComprehension self() { return this; } 063 064 /** 065 * Defines the expression of the comprehension. 066 * 067 * <code>2*x + y</code> in the previous example. 068 */ 069 public CollectionComprehension expression(Object expression) { 070 this.expression = makeParentOf(ExpressionStatement.of(expression)); 071 return this; 072 } 073 074 /** 075 * Adds a loop instruction to this comprehension. 076 * 077 * <p>The object to add is a loop-like expression, <code>foreach x in someIterable</code> and 078 * <code>for (var y=0, y < 5, y = y + 1)</code> in the previous example. 079 * @param loop a {@link ForEachLoopStatement} or a {@link LoopStatement}. 080 */ 081 public CollectionComprehension loop(Object loop) { 082 if (!(loop instanceof ForEachLoopStatement || loop instanceof LoopStatement)) { 083 throw new IllegalArgumentException("Loop expected, got a " + loop.getClass().getName()); 084 } 085 this.loopBlocks.add(makeParentOf(GoloStatement.of(loop))); 086 return this; 087 } 088 089 public ExpressionStatement<?> expression() { 090 return this.expression; 091 } 092 093 public List<GoloStatement<?>> loops() { 094 return unmodifiableList(loopBlocks); 095 } 096 097 public CollectionLiteral.Type getType() { 098 return this.type; 099 } 100 101 public CollectionLiteral.Type getMutableType() { 102 return (CollectionLiteral.Type.tuple.equals(type) 103 || CollectionLiteral.Type.array.equals(type)) 104 ? CollectionLiteral.Type.list 105 : type; 106 } 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override 112 public void accept(GoloIrVisitor visitor) { 113 visitor.visitCollectionComprehension(this); 114 } 115 116 /** 117 * {@inheritDoc} 118 */ 119 @Override 120 public List<GoloElement<?>> children() { 121 LinkedList<GoloElement<?>> children = new LinkedList<>(); 122 children.add(expression); 123 children.addAll(loopBlocks); 124 return children; 125 } 126 127 /** 128 * {@inheritDoc} 129 */ 130 @Override 131 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 132 if (expression == original && newElement instanceof ExpressionStatement) { 133 expression(newElement); 134 } else if (newElement instanceof Block && loopBlocks.contains(original)) { 135 loopBlocks.set(loopBlocks.indexOf(original), Block.of(newElement)); 136 } else { 137 throw cantReplace(original, newElement); 138 } 139 } 140}