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}