Skip to content

Commit

Permalink
Merge pull request #962 from lf-lang/inheritance-cleanups
Browse files Browse the repository at this point in the history
Inheritance cleanups
  • Loading branch information
edwardalee authored Feb 15, 2022
2 parents 7ff006a + 3b825a0 commit 0cf3248
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 84 deletions.
171 changes: 96 additions & 75 deletions org.lflang/src/org/lflang/ASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@

package org.lflang;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
Expand All @@ -47,12 +48,11 @@
import org.eclipse.xtext.nodemodel.impl.HiddenLeafNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.xbase.lib.CollectionExtensions;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.IteratorExtensions;
import org.eclipse.xtext.xbase.lib.StringExtensions;
import org.lflang.generator.GeneratorBase;
import org.lflang.generator.CodeMap;
import org.lflang.generator.GeneratorBase;
import org.lflang.generator.InvalidSourceException;
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
Expand Down Expand Up @@ -84,6 +84,9 @@
import org.lflang.lf.WidthSpec;
import org.lflang.lf.WidthTerm;

import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

/**
* A helper class for modifying and analyzing the AST.
* @author{Marten Lohstroh <[email protected]>}
Expand Down Expand Up @@ -447,13 +450,7 @@ public static String getUniqueIdentifier(Reactor reactor, String name) {
* @param definition Reactor class definition.
*/
public static List<Action> allActions(Reactor definition) {
List<Action> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allActions(toDefinition(base)));
}
result.addAll(definition.getActions());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getActions());
}

/**
Expand All @@ -462,28 +459,19 @@ public static List<Action> allActions(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<Connection> allConnections(Reactor definition) {
List<Connection> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allConnections(toDefinition(base)));
}
result.addAll(definition.getConnections());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getConnections());
}

/**
* Given a reactor class, return a list of all its inputs,
* which includes inputs of base classes that it extends.
* If the base classes include a cycle, where X extends Y and Y extends X,
* then return only the input defined in the base class.
* The returned list may be empty.
* @param definition Reactor class definition.
*/
public static List<Input> allInputs(Reactor definition) {
List<Input> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allInputs(toDefinition(base)));
}
result.addAll(definition.getInputs());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getInputs());
}

/**
Expand All @@ -492,13 +480,7 @@ public static List<Input> allInputs(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<Instantiation> allInstantiations(Reactor definition) {
List<Instantiation> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allInstantiations(toDefinition(base)));
}
result.addAll(definition.getInstantiations());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getInstantiations());
}

/**
Expand All @@ -507,13 +489,7 @@ public static List<Instantiation> allInstantiations(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<Output> allOutputs(Reactor definition) {
List<Output> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allOutputs(toDefinition(base)));
}
result.addAll(definition.getOutputs());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getOutputs());
}

/**
Expand All @@ -522,13 +498,7 @@ public static List<Output> allOutputs(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<Parameter> allParameters(Reactor definition) {
List<Parameter> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allParameters(toDefinition(base)));
}
result.addAll(definition.getParameters());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getParameters());
}

/**
Expand All @@ -537,13 +507,7 @@ public static List<Parameter> allParameters(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<Reaction> allReactions(Reactor definition) {
List<Reaction> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allReactions(toDefinition(base)));
}
result.addAll(definition.getReactions());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getReactions());
}

/**
Expand All @@ -552,13 +516,7 @@ public static List<Reaction> allReactions(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<StateVar> allStateVars(Reactor definition) {
List<StateVar> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allStateVars(toDefinition(base)));
}
result.addAll(definition.getStateVars());
return result;
return ASTUtils.collectElements(definition, (Reactor r) -> r.getStateVars());
}

/**
Expand All @@ -567,12 +525,43 @@ public static List<StateVar> allStateVars(Reactor definition) {
* @param definition Reactor class definition.
*/
public static List<Timer> allTimers(Reactor definition) {
List<Timer> result = new ArrayList<>();
List<ReactorDecl> superClasses = convertToEmptyListIfNull(definition.getSuperClasses());
for (ReactorDecl base : superClasses) {
result.addAll(allTimers(toDefinition(base)));
return ASTUtils.collectElements(definition, (Reactor r) -> r.getTimers());
}

/**
* Return all the superclasses of the specified reactor
* in deepest-first order. For example, if A extends B and C, and
* B and C both extend D, this will return the list [D, B, C, A].
* Duplicates are removed. If the specified reactor does not extend
* any other reactor, then return an empty list.
* If a cycle is found, where X extends Y and Y extends X, or if
* a superclass is declared that is not found, then return null.
* @param reactor The specified reactor.
*/
public static LinkedHashSet<Reactor> superClasses(Reactor reactor) {
return superClasses(reactor, new LinkedHashSet<Reactor>());
}

/**
* Collect elements of type T from the class hierarchy defined by
* a given reactor definition.
* @param definition The reactor definition.
* @param elements A function that maps a reactor definition to a list of
* elements of type T.
* @param <T> The type of elements to collect (e.g., Port, Timer, etc.)
* @return
*/
public static <T> List<T> collectElements(Reactor definition, Function<Reactor,List<T>> elements) {
List<T> result = new ArrayList<T>();
// Add elements of elements defined in superclasses.
LinkedHashSet<Reactor> s = superClasses(definition);
if (s != null) {
for (Reactor superClass : s) {
result.addAll(elements.apply(superClass));
}
}
result.addAll(definition.getTimers());
// Add elements of the current reactor.
result.addAll(elements.apply(definition));
return result;
}

Expand Down Expand Up @@ -894,7 +883,7 @@ public static String baseType(Type type) {

/**
* Report whether the given literal is zero or not.
* @param literalOrCode AST node to inspect.
* @param literal AST node to inspect.
* @return True if the given literal denotes the constant `0`, false
* otherwise.
*/
Expand Down Expand Up @@ -997,7 +986,7 @@ public static boolean isValidTime(Value value) {

/**
* Report whether the given time denotes a valid time or not.
* @param value AST node to inspect.
* @param t AST node to inspect.
* @return True if the argument denotes a valid time, false otherwise.
*/
public static boolean isValidTime(Time t) {
Expand All @@ -1010,7 +999,7 @@ public static boolean isValidTime(Time t) {
/**
* Report whether the given parameter denotes time list, meaning it is a list
* of which all elements are valid times.
* @param value AST node to inspect.
* @param p AST node to inspect.
* @return True if the argument denotes a valid time list, false otherwise.
*/
// TODO: why does this function always return true ???
Expand Down Expand Up @@ -1094,7 +1083,7 @@ public static boolean isValidTimeList(Parameter p) {
* ```
*
* @param parameter The parameter.
* @param instantiation The (optional) instantiation.
* @param instantiations The (optional) list of instantiations.
*
* @return The value of the parameter.
*
Expand Down Expand Up @@ -1161,7 +1150,7 @@ public static List<Value> initialValue(Parameter parameter, List<Instantiation>
* belongs to the specified instantiation, meaning that it is defined in
* the reactor class being instantiated or one of its base classes.
* @param eobject The object.
* @param instnatiation The instantiation.
* @param instantiation The instantiation.
*/
public static boolean belongsTo(EObject eobject, Instantiation instantiation) {
Reactor reactor = toDefinition(instantiation.getReactorClass());
Expand All @@ -1173,7 +1162,7 @@ public static boolean belongsTo(EObject eobject, Instantiation instantiation) {
* belongs to the specified reactor, meaning that it is defined in
* reactor class or one of its base classes.
* @param eobject The object.
* @param instnatiation The instantiation.
* @param reactor The reactor.
*/
public static boolean belongsTo(EObject eobject, Reactor reactor) {
if (eobject.eContainer() == reactor) return true;
Expand All @@ -1190,7 +1179,7 @@ public static boolean belongsTo(EObject eobject, Reactor reactor) {
* if it does not have an integer value.
* If the value of the parameter is a list of integers,
* return the sum of value in the list.
* The instantiations parameter is as in
* The instantiations parameter is as in
* {@link initialValue(Parameter, List<Instantiation>)}.
*
* @param parameter The parameter.
Expand Down Expand Up @@ -1490,7 +1479,7 @@ public static boolean isGeneric(Reactor r) {
* return the imported reactor class definition. Otherwise,
* just return the argument.
* @param r A Reactor or an ImportedReactor.
* @return The Reactor class definition.
* @return The Reactor class definition or null if no definition is found.
*/
public static Reactor toDefinition(ReactorDecl r) {
if (r == null)
Expand Down Expand Up @@ -1640,11 +1629,43 @@ public static TargetDecl targetDecl(Model model) {
public static TargetDecl targetDecl(Resource model) {
return IteratorExtensions.head(Iterators.filter(model.getAllContents(), TargetDecl.class));
}


/////////////////////////////////////////////////////////
//// Private methods

/**
* Returns the list if it is not null. Otherwise return an empty list.
*/
private static <T> List<T> convertToEmptyListIfNull(List<T> list) {
return list != null ? list : new ArrayList<>();
}

/**
* Return all the superclasses of the specified reactor
* in deepest-first order. For example, if A extends B and C, and
* B and C both extend D, this will return the list [D, B, C, A].
* Duplicates are removed. If the specified reactor does not extend
* any other reactor, then return an empty list.
* If a cycle is found, where X extends Y and Y extends X, or if
* a superclass is declared that is not found, then return null.
* @param reactor The specified reactor.
* @param extensions A set of reactors extending the specified reactor
* (used to detect circular extensions).
*/
private static LinkedHashSet<Reactor> superClasses(Reactor reactor, Set<Reactor> extensions) {
LinkedHashSet<Reactor> result = new LinkedHashSet<Reactor>();
for (ReactorDecl superDecl : convertToEmptyListIfNull(reactor.getSuperClasses())) {
Reactor r = toDefinition(superDecl);
if (r == reactor || r == null) return null;
// If r is in the extensions, then we have a circular inheritance structure.
if (extensions.contains(r)) return null;
extensions.add(r);
LinkedHashSet<Reactor> baseExtends = superClasses(r, extensions);
extensions.remove(r);
if (baseExtends == null) return null;
result.addAll(baseExtends);
result.add(r);
}
return result;
}
}
Loading

0 comments on commit 0cf3248

Please sign in to comment.