Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inheritance cleanups #962

Merged
merged 5 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <marten@berkeley.edu>}
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