001package org.clafer.ast; 002 003import gnu.trove.map.TIntObjectMap; 004import gnu.trove.map.hash.TIntObjectHashMap; 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.Collection; 008import java.util.Collections; 009import java.util.Comparator; 010import java.util.HashSet; 011import java.util.Iterator; 012import java.util.List; 013import java.util.Set; 014import org.clafer.ast.analysis.AnalysisException; 015import org.clafer.common.Check; 016import org.clafer.graph.GraphUtil; 017import org.clafer.graph.KeyGraph; 018import org.clafer.graph.Vertex; 019 020/** 021 * Various static utility functions for working with AST. 022 * 023 * @author jimmy 024 */ 025public class AstUtil { 026 027 private AstUtil() { 028 } 029 030 /** 031 * Find all the nested Clafers in no specific order. 032 * 033 * @param model the Clafer model 034 * @return the Clafers in the model 035 */ 036 public static List<AstClafer> getClafers(AstModel model) { 037 List<AstClafer> clafers = new ArrayList<>(); 038 clafers.add(model); 039 for (AstAbstractClafer abstractClafer : model.getAbstracts()) { 040 clafers.add(abstractClafer); 041 getNestedChildClafers(abstractClafer, clafers); 042 } 043 for (AstConcreteClafer child : model.getChildren()) { 044 clafers.add(child); 045 getNestedChildClafers(child, clafers); 046 } 047 return clafers; 048 } 049 050 /** 051 * Find all the nested concrete Clafers in no specific order. 052 * 053 * @param model the Clafer model 054 * @return the concrete Clafers in the model excluding the root 055 */ 056 public static List<AstConcreteClafer> getConcreteClafers(AstModel model) { 057 List<AstConcreteClafer> clafers = new ArrayList<>(); 058 clafers.add(model); 059 for (AstAbstractClafer abstractClafer : model.getAbstracts()) { 060 getNestedChildClafers(abstractClafer, clafers); 061 } 062 for (AstConcreteClafer child : model.getChildren()) { 063 clafers.add(child); 064 getNestedChildClafers(child, clafers); 065 } 066 return clafers; 067 } 068 069 /** 070 * Find the Clafers such that the parents appear before the children, and 071 * the subclafers appear before the superclafers. 072 * 073 * @param model the Clafer model 074 * @return the Clafers in the above order 075 */ 076 public static List<Set<AstClafer>> getClafersInParentAndSubOrder(AstModel model) { 077 KeyGraph<AstClafer> dependency = new KeyGraph<>(); 078 for (AstAbstractClafer abstractClafer : model.getAbstracts()) { 079 Vertex<AstClafer> node = dependency.getVertex(abstractClafer); 080 for (AstClafer sub : abstractClafer.getSubs()) { 081 node.addNeighbour(dependency.getVertex(sub)); 082 } 083 } 084 for (AstConcreteClafer concreteClafer : getConcreteClafers(model)) { 085 if (concreteClafer.hasParent()) { 086 dependency.addEdge(concreteClafer, concreteClafer.getParent()); 087 } 088 } 089 return GraphUtil.computeStronglyConnectedComponents(dependency); 090 } 091 092 /** 093 * Find the abstract Clafers such that the subclafers appear before the 094 * super clafers. 095 * 096 * @param model the Clafer model 097 * @return the abstract Clafers in the above order 098 */ 099 public static List<AstAbstractClafer> getAbstractClafersInSubOrder(AstModel model) { 100 List<AstAbstractClafer> clafers = new ArrayList<>(model.getAbstracts()); 101 Collections.sort(clafers, new Comparator<AstAbstractClafer>() { 102 @Override 103 public int compare(AstAbstractClafer o1, AstAbstractClafer o2) { 104 int depth1 = AstUtil.getSuperHierarchy(o1).size(); 105 int depth2 = AstUtil.getSuperHierarchy(o2).size(); 106 return depth1 > depth2 ? -1 : (depth1 == depth2 ? 0 : 1); 107 } 108 }); 109 return clafers; 110 } 111 112 /** 113 * Find the concrete Clafers such that the parents appear before the 114 * children. 115 * 116 * @param model the Clafer model 117 * @return the concrete Clafers in the above order 118 */ 119 public static List<AstConcreteClafer> getConcreteClafersInParentOrder(AstModel model) { 120 // The current implementation of getConreteClafers implements parent order. 121 return getConcreteClafers(model); 122 } 123 124 /** 125 * Detects if the Clafer is the implicit root of the model. 126 * 127 * @param clafer the Clafer 128 * @return {@code true} if and only if the Clafer is the root, {@code false} 129 * otherwise 130 */ 131 public static boolean isRoot(AstConcreteClafer clafer) { 132 return clafer instanceof AstModel; 133 } 134 135 /** 136 * Detects if the Clafer is the implicit type root of the model. 137 * 138 * @param clafer the Clafer 139 * @return {@code true} if and only if the Clafer is the type root, 140 * {@code false} otherwise 141 */ 142 public static boolean isTypeRoot(AstAbstractClafer clafer) { 143 return clafer instanceof AstAbstractClafer && !clafer.hasSuperClafer(); 144 } 145 146 /** 147 * Detects if the Clafer is directly under the root. 148 * 149 * @param clafer the Clafer 150 * @return {@code true} if and only if the Clafer is a top Clafer, 151 * {@code false} otherwise 152 */ 153 public static boolean isTop(AstClafer clafer) { 154 if (clafer instanceof AstConcreteClafer) { 155 AstConcreteClafer concrete = (AstConcreteClafer) clafer; 156 return concrete.getParent() instanceof AstConcreteClafer 157 && isRoot((AstConcreteClafer) concrete.getParent()); 158 } 159 return clafer instanceof AstAbstractClafer; 160 } 161 162 /** 163 * Find the highest non-root Clafer above the supplied one. 164 * 165 * @param clafer the Clafer 166 * @return the highest non-root ancestor 167 */ 168 public static AstClafer getTopParent(AstClafer clafer) { 169 if (clafer instanceof AstConcreteClafer) { 170 AstConcreteClafer concrete = (AstConcreteClafer) clafer; 171 if (!concrete.hasParent()) { 172 throw new IllegalArgumentException("Root does not have a non-root parent."); 173 } 174 if (!(concrete.getParent() instanceof AstConcreteClafer 175 && isRoot((AstConcreteClafer) concrete.getParent()))) { 176 return getTopParent(concrete.getParent()); 177 } 178 } 179 return clafer; 180 } 181 182 /** 183 * Find all the Clafers below the supplied one, including itself. 184 * 185 * @param clafer the Clafer 186 * @return the nested Clafers 187 */ 188 public static List<AstClafer> getNestedClafers(AstClafer clafer) { 189 List<AstClafer> clafers = new ArrayList<>(); 190 clafers.add(clafer); 191 getNestedChildClafers(clafer, clafers); 192 return clafers; 193 } 194 195 private static void getNestedChildClafers(AstClafer clafer, List<? super AstConcreteClafer> clafers) { 196 for (AstConcreteClafer child : clafer.getChildren()) { 197 clafers.add(child); 198 getNestedChildClafers(child, clafers); 199 } 200 } 201 202 /** 203 * Find all the concrete subtypes of the supplied Clafer, including itself 204 * if it is concrete. 205 * 206 * @param clafer the Clafer 207 * @return the concrete sub-Clafers. 208 */ 209 public static List<AstConcreteClafer> getConcreteSubs(AstClafer clafer) { 210 if (clafer instanceof AstConcreteClafer) { 211 return Collections.singletonList((AstConcreteClafer) clafer); 212 } 213 List<AstConcreteClafer> subs = new ArrayList<>(); 214 getConcreteSubs(clafer, subs); 215 return subs; 216 } 217 218 private static void getConcreteSubs(AstClafer sub, Collection<AstConcreteClafer> subs) { 219 if (sub instanceof AstAbstractClafer) { 220 AstAbstractClafer sup = (AstAbstractClafer) sub; 221 for (AstClafer subsub : sup.getSubs()) { 222 getConcreteSubs(subsub, subs); 223 } 224 } else { 225 subs.add((AstConcreteClafer) sub); 226 } 227 } 228 229 /** 230 * Find all the constraints in the model. 231 * 232 * @param model the model 233 * @return the nested constraints 234 */ 235 public static List<AstConstraint> getNestedConstraints(AstModel model) { 236 List<AstConstraint> constraints = new ArrayList<>(); 237 for (AstAbstractClafer abstractClafer : model.getAbstracts()) { 238 getNestedConstraints(abstractClafer, constraints); 239 } 240 getNestedConstraints(model, constraints); 241 return constraints; 242 } 243 244 private static void getNestedConstraints(AstClafer clafer, List<AstConstraint> constraints) { 245 constraints.addAll(clafer.getConstraints()); 246 for (AstConcreteClafer child : clafer.getChildren()) { 247 getNestedConstraints(child, constraints); 248 } 249 } 250 251 /** 252 * Finds all the supertypes of the Clafer in order of lowest to highest. 253 * 254 * @param clafer the Clafer 255 * @return the supertypes 256 */ 257 public static List<AstAbstractClafer> getSupers(final AstClafer clafer) { 258 List<AstAbstractClafer> supers = new ArrayList<>(); 259 AstAbstractClafer sup = clafer.getSuperClafer(); 260 while (sup != null) { 261 supers.add(sup); 262 sup = sup.getSuperClafer(); 263 } 264 return supers; 265 } 266 267 /** 268 * Finds the supertype hierarchy of the Clafer. Equivalent to the Haskell 269 * code {@code clafer : getSupers clafer}. 270 * 271 * @param clafer the Clafer 272 * @return the supertype hierarchy 273 * @throws AnalysisException a cycle in the type hierarchy 274 */ 275 public static List<AstClafer> getSuperHierarchy(final AstClafer clafer) throws AnalysisException { 276 List<AstClafer> supers = new ArrayList<>(); 277 AstClafer sup = clafer; 278 while (sup != null) { 279 if (supers.contains(sup)) { 280 throw new AnalysisException("Cycle in type hierarchy " + supers); 281 } 282 supers.add(sup); 283 sup = sup.getSuperClafer(); 284 } 285 return supers; 286 } 287 288 /** 289 * Detects if a set of {@code from} is also a set of {@code to}. 290 * 291 * @param from the subtype 292 * @param to the supertype 293 * @return {@code true} if and only if to is a supertype of from, 294 * {@code false} otherwise 295 */ 296 public static boolean isAssignable(AstClafer from, AstClafer to) { 297 return to.equals(from) 298 || (to instanceof AstAbstractClafer 299 && getSupers(from).contains((AstAbstractClafer) to)); 300 } 301 302 /** 303 * Detects if a set of {@code t1} can share elements with a set of 304 * {@code t2}. 305 * 306 * @param t1 first type 307 * @param t2 second type 308 * @return {@code true} if and only if the first and second type intersect, 309 * {@code false} otherwise 310 */ 311 public static boolean hasNonEmptyIntersectionType(AstClafer t1, AstClafer t2) { 312 if (t1.equals(t2)) { 313 return true; 314 } 315 if (t1 instanceof AstAbstractClafer 316 && getSupers(t2).contains((AstAbstractClafer) t1)) { 317 return true; 318 } 319 if (t2 instanceof AstAbstractClafer 320 && getSupers(t1).contains((AstAbstractClafer) t2)) { 321 return true; 322 } 323 return false; 324 } 325 326 /** 327 * Detects if the union type is fully covered by the partitions. For 328 * example, 329 * {@code isUnionType(Animal, Arrays.asList(Primate, Human, Dolphin))} would 330 * return {@code true} if and only if primates, humans, and dolphins are the 331 * only animals. If humans are primates, the result would still hold. 332 * 333 * @param union the union type 334 * @param partitions the partition of the union type 335 * @return {@code true} if and only if the partitions fully cover the union 336 * type, {@code false} otherwise 337 */ 338 public static boolean isUnionType(AstClafer union, AstClafer[] partitions) { 339 Set<AstConcreteClafer> unionSubs = new HashSet<>(); 340 getConcreteSubs(union, unionSubs); 341 Set<AstConcreteClafer> partitionSubs = new HashSet<>(); 342 for (AstClafer partition : partitions) { 343 getConcreteSubs(partition, partitionSubs); 344 } 345 return unionSubs.equals(partitionSubs); 346 } 347 348 private static List<AstClafer> getUnionTypeHierarchy( 349 List<AstClafer> typeHierarchy1, List<AstClafer> typeHierarchy2) { 350 if (typeHierarchy1.size() > typeHierarchy2.size()) { 351 return getUnionTypeHierarchy(typeHierarchy2, typeHierarchy1); 352 } 353 for (int i = 1; i <= typeHierarchy1.size(); i++) { 354 if (!typeHierarchy1.get(typeHierarchy1.size() - i).equals( 355 typeHierarchy2.get(typeHierarchy2.size() - i))) { 356 return typeHierarchy1.subList(typeHierarchy1.size() - i + 1, typeHierarchy1.size()); 357 } 358 } 359 return typeHierarchy1; 360 } 361 362 /** 363 * Finds the lowest common supertype. 364 * 365 * @param unionType the union type 366 * @return the lowest common supertype of {@code unionType} 367 */ 368 public static AstClafer getLowestCommonSupertype(Iterable<AstClafer> unionType) { 369 Iterator<AstClafer> iter = unionType.iterator(); 370 if (!iter.hasNext()) { 371 throw new IllegalArgumentException(); 372 } 373 AstClafer first = iter.next(); 374 if (!iter.hasNext()) { 375 return first; 376 } 377 List<AstClafer> supers = getSuperHierarchy(first); 378 while (iter.hasNext()) { 379 List<AstClafer> otherSupers = getSuperHierarchy(iter.next()); 380 supers = getUnionTypeHierarchy(supers, otherSupers); 381 if (supers.isEmpty()) { 382 return null; 383 } 384 } 385 return supers.get(0); 386 } 387 388 /** 389 * Finds the lowest common supertype. 390 * 391 * @param unionType the union type 392 * @return the lowest common supertype of {@code unionType} 393 */ 394 public static AstClafer getLowestCommonSupertype(AstClafer... unionType) { 395 return getLowestCommonSupertype(Arrays.asList(unionType)); 396 } 397 398 public static boolean hasInheritedRef(AstClafer clafer) { 399 return getInheritedRef(clafer) != null; 400 } 401 402 /** 403 * Find the reference belonging to the Clafer or the reference it inherited. 404 * 405 * @param clafer the Clafer 406 * @return the reference, or {@code null} if none exist 407 */ 408 public static AstRef getInheritedRef(AstClafer clafer) { 409 AstClafer superClafer = Check.notNull(clafer); 410 do { 411 if (superClafer.hasRef()) { 412 return superClafer.getRef(); 413 } 414 superClafer = superClafer.getSuperClafer(); 415 } while (superClafer != null); 416 return null; 417 } 418 419 /** 420 * Retrieve the names of the variables. Use the names for error messages 421 * rather than {@link Object#toString}. 422 * 423 * @param vars the variables 424 * @return the names of the variables 425 */ 426 public static String[] getNames(AstVar... vars) { 427 String[] names = new String[vars.length]; 428 for (int i = 0; i < names.length; i++) { 429 names[i] = vars[i].getName(); 430 } 431 return names; 432 } 433 434 /** 435 * Retrieve the names of the Clafers. Use the names for error messages 436 * rather than {@link Object#toString}. 437 * 438 * @param vars the variables 439 * @return the names of the variables 440 */ 441 public static List<String> getNames(Iterable<? extends AstVar> vars) { 442 List<String> names = new ArrayList<>(); 443 for (AstVar var : vars) { 444 names.add(var.getName()); 445 } 446 return names; 447 } 448}