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

Clean up AttributeUtils #1470

Merged
merged 6 commits into from
Nov 21, 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
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@

import javax.inject.Inject;

import org.eclipse.elk.alg.layered.options.EdgeStraighteningStrategy;
import org.eclipse.elk.alg.layered.options.FixedAlignment;
import org.eclipse.elk.alg.layered.options.GreedySwitchType;
import org.eclipse.elk.alg.layered.options.LayerConstraint;
import org.eclipse.elk.alg.layered.options.LayeredOptions;
import org.eclipse.elk.alg.layered.options.NodePlacementStrategy;
Expand Down Expand Up @@ -1390,7 +1387,7 @@ private KNode addErrorComment(KNode node, String message) {

private Iterable<KNode> createUserComments(EObject element, KNode targetNode) {
if (getBooleanValue(SHOW_USER_LABELS)) {
String commentText = AttributeUtils.label(element);
String commentText = AttributeUtils.getLabel(element);

if (!StringExtensions.isNullOrEmpty(commentText)) {
KNode comment = _kNodeExtensions.createNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,18 @@
***************/
package org.lflang.diagram.synthesis.util;

import java.io.InputStream;
import java.util.HashMap;

//import org.eclipse.swt.graphics.ImageData;
//import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.xtext.xbase.lib.Extension;
import org.lflang.ASTUtils;

import org.lflang.AttributeUtils;
import org.lflang.diagram.synthesis.AbstractSynthesisExtensions;
import org.lflang.lf.ReactorDecl;
import org.lflang.util.FileUtil;

import com.google.inject.Inject;

import de.cau.cs.kieler.klighd.krendering.Colors;
import de.cau.cs.kieler.klighd.krendering.KContainerRendering;
import de.cau.cs.kieler.klighd.krendering.KGridPlacementData;
import de.cau.cs.kieler.klighd.krendering.KRectangle;
import de.cau.cs.kieler.klighd.krendering.ViewSynthesisShared;
import de.cau.cs.kieler.klighd.krendering.extensions.KContainerRenderingExtensions;
import de.cau.cs.kieler.klighd.krendering.extensions.KRenderingExtensions;
Expand Down Expand Up @@ -74,10 +68,7 @@ public void handleIcon(KContainerRendering rendering, ReactorDecl reactor, boole
error = null;

// Get annotation
String iconPath = AttributeUtils.findAttributeByName(reactor, "icon");
if (iconPath == null) { // Fallback to old syntax (in comment)
iconPath = ASTUtils.findAnnotationInComments(reactor, "@icon");
}
var iconPath = AttributeUtils.getIconPath(reactor);
if (iconPath != null && !iconPath.isEmpty()) {
var iconLocation = FileUtil.locateFile(iconPath, reactor.eResource());
if (iconLocation == null) {
Expand Down
47 changes: 24 additions & 23 deletions org.lflang/src/org/lflang/ASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.impl.HiddenLeafNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
Expand Down Expand Up @@ -1026,6 +1025,30 @@ public static boolean isInteger(String literal) {
return true;
}

/**
* Report whether the given string literal is a boolean value or not.
* @param literal AST node to inspect.
* @return True if the given value is a boolean, false otherwise.
*/
public static boolean isBoolean(String literal) {
return literal.equalsIgnoreCase("true") || literal.equalsIgnoreCase("false");
}

/**
* Report whether the given string literal is a float value or not.
* @param literal AST node to inspect.
* @return True if the given value is a float, false otherwise.
*/
public static boolean isFloat(String literal) {
try {
//noinspection ResultOfMethodCallIgnored
Float.parseFloat(literal);
} catch (NumberFormatException e) {
return false;
}
return true;
}

/**
* Report whether the given code is an integer number or not.
* @param code AST node to inspect.
Expand Down Expand Up @@ -1772,28 +1795,6 @@ public static Predicate<INode> sameLine(ICompositeNode compNode) {
return false;
};
}

/**
* Retrieve a specific annotation in a comment associated with the given model element in the AST.
*
* This will look for a comment. If one is found, it searches for the given annotation `key`.
* and extracts any string that follows the annotation marker.
*
* @param object the AST model element to search a comment for
* @param key the specific annotation key to be extracted
* @return `null` if no JavaDoc style comment was found or if it does not contain the given key.
* The string immediately following the annotation marker otherwise.
*/
public static String findAnnotationInComments(EObject object, String key) {
if (!(object.eResource() instanceof XtextResource)) return null;
ICompositeNode node = NodeModelUtils.findActualNodeFor(object);
return getPrecedingComments(node, n -> true).flatMap(String::lines)
.filter(line -> line.contains(key))
.map(String::trim)
.map(it -> it.substring(it.indexOf(key) + key.length()))
.map(it -> it.endsWith("*/") ? it.substring(0, it.length() - "*/".length()) : it)
.findFirst().orElse(null);
}

/**
* Find the main reactor and set its name if none was defined.
Expand Down
2 changes: 1 addition & 1 deletion org.lflang/src/org/lflang/AstExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ val Resource.model: Model get() = this.allContents.asSequence().filterIsInstance
* If the reaction is annotated with a label, then the label is returned. Otherwise, a reaction name
* is generated based on its priority.
*/
val Reaction.label get(): String = AttributeUtils.label(this) ?: "reaction_$priority"
val Reaction.label get(): String = AttributeUtils.getLabel(this) ?: "reaction_$priority"

/** Get the priority of a receiving reaction */
val Reaction.priority
Expand Down
98 changes: 67 additions & 31 deletions org.lflang/src/org/lflang/AttributeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,21 @@
import java.util.List;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;

import org.lflang.lf.Action;
import org.lflang.lf.Attribute;
import org.lflang.lf.Input;
import org.lflang.lf.Instantiation;
import org.lflang.lf.Output;
import org.lflang.lf.Parameter;
import org.lflang.lf.Reaction;
import org.lflang.lf.Reactor;
import org.lflang.lf.StateVar;
import org.lflang.lf.Timer;
import org.lflang.util.StringUtil;

/**
* A helper class for processing attributes in the AST.
Expand Down Expand Up @@ -71,33 +76,78 @@ public static List<Attribute> getAttributes(EObject node) {
return ((Input) node).getAttributes();
} else if (node instanceof Output) {
return ((Output) node).getAttributes();
} else if (node instanceof Instantiation) {
return ((Instantiation) node).getAttributes();
}
throw new IllegalArgumentException("Not annotatable: " + node);
}

/**
* Return the value of the attribute with the given name
* Return the attribute with the given name
* if present, otherwise return null.
*
* @throws IllegalArgumentException If the node cannot have attributes
*/
public static String findAttributeByName(EObject node, String name) {
public static Attribute findAttributeByName(EObject node, String name) {
List<Attribute> attrs = getAttributes(node);
return attrs.stream()
.filter(it -> it.getAttrName().equalsIgnoreCase(name)) // case-insensitive search (more user-friendly)
.map(it -> it.getAttrParms().get(0).getValue().getStr())
.findFirst()
.orElse(null);
}

/**
* Return the value of the {@code @label} attribute if
* present, otherwise return null.
* Return the first argument specified for the attribute.
*
* @throws IllegalArgumentException If the node cannot have attributes
* This should be used if the attribute is expected to have a single argument.
* If there is no argument, null is returned.
*/
public static String getFirstArgumentValue(Attribute attr) {
if (attr == null || attr.getAttrParms().isEmpty()) {
return null;
}
return StringUtil.removeQuotes(attr.getAttrParms().get(0).getValue());
}

/**
* Search for an attribute with the given name on the given AST node and return its first
* argument as a String.
*
* This should only be used on attributes that are expected to have a single argument.
*
* Returns null if the attribute is not found or if it does not have any arguments.
*/
public static String getAttributeValue(EObject node, String attrName) {
final var attr = findAttributeByName(node, attrName);
String value = getFirstArgumentValue(attr);
// Attribute annotations in comments are deprecated, but we still check for then for backwards
// compatibility
if (value == null) {
return findAnnotationInComments(node, "@" + attrName);
}
return value;
}

/**
* Retrieve a specific annotation in a comment associated with the given model element in the AST.
*
* This will look for a comment. If one is found, it searches for the given annotation `key`.
* and extracts any string that follows the annotation marker.
*
* @param object the AST model element to search a comment for
* @param key the specific annotation key to be extracted
* @return `null` if no JavaDoc style comment was found or if it does not contain the given key.
* The string immediately following the annotation marker otherwise.
*/
public static String findLabelAttribute(EObject node) {
return findAttributeByName(node, "label");
public static String findAnnotationInComments(EObject object, String key) {
if (!(object.eResource() instanceof XtextResource)) return null;
ICompositeNode node = NodeModelUtils.findActualNodeFor(object);
return ASTUtils.getPrecedingComments(node, n -> true).flatMap(String::lines)
.filter(line -> line.contains(key))
.map(String::trim)
.map(it -> it.substring(it.indexOf(key) + key.length()))
.map(it -> it.endsWith("*/") ? it.substring(0, it.length() - "*/".length()) : it)
.findFirst().orElse(null);
}

/**
Expand All @@ -106,35 +156,21 @@ public static String findLabelAttribute(EObject node) {
* @param node An AST node.
*/
public static boolean isSparse(EObject node) {
if (node instanceof Input) {
for (var attribute : getAttributes(node)) {
if (attribute.getAttrName().equalsIgnoreCase("sparse")) return true;
}
}
return false;
return findAttributeByName(node, "sparse") != null;
}

/**
* Return the declared label of the node, as given by the @label
* annotation (or an @label comment).
*
* @throws IllegalArgumentException If the node cannot have attributes
* Return the declared label of the node, as given by the @label annotation.
*/
public static String label(EObject n) {
String fromAttr = findLabelAttribute(n);
if (fromAttr == null) {
return ASTUtils.findAnnotationInComments(n, "@label");
}
return fromAttr;
public static String getLabel(EObject node) {
return getAttributeValue(node, "label");
}

/**
* Search for an `@label` annotation for a given reaction.
*
* @param n the reaction for which the label should be searched
* @return The annotated string if an `@label` annotation was found. `null` otherwise.
* Return the declared icon of the node, as given by the @icon annotation.
*/
public static String label(Reaction n) {
return label((EObject) n);
public static String getIconPath(EObject node) {
return getAttributeValue(node, "icon");
}

}
10 changes: 2 additions & 8 deletions org.lflang/src/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Preamble:
(visibility=Visibility)? 'preamble' code=Code;

Instantiation:
(attributes+=Attribute)*
name=ID '=' 'new' (widthSpec=WidthSpec)?
reactorClass=[ReactorDecl] ('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')? '('
(parameters+=Assignment (',' parameters+=Assignment)*)?
Expand All @@ -245,14 +246,7 @@ Attribute:
;

AttrParm:
(name=ID '=')? value=AttrParmValue;

AttrParmValue:
str=STRING
| int=SignedInt
| bool=Boolean
| float=SignedFloat
;
(name=ID '=')? value=Literal;

/////////// For target parameters

Expand Down
13 changes: 1 addition & 12 deletions org.lflang/src/org/lflang/ast/IsEqual.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.lflang.lf.ArraySpec;
import org.lflang.lf.Assignment;
import org.lflang.lf.AttrParm;
import org.lflang.lf.AttrParmValue;
import org.lflang.lf.Attribute;
import org.lflang.lf.BuiltinTriggerRef;
import org.lflang.lf.Code;
Expand Down Expand Up @@ -260,17 +259,7 @@ public Boolean caseAttribute(Attribute object) {
public Boolean caseAttrParm(AttrParm object) {
return new ComparisonMachine<>(object, AttrParm.class)
.equalAsObjects(AttrParm::getName)
.equivalent(AttrParm::getValue)
.conclusion;
}

@Override
public Boolean caseAttrParmValue(AttrParmValue object) {
return new ComparisonMachine<>(object, AttrParmValue.class)
.equalAsObjects(AttrParmValue::getBool)
.equalAsObjects(AttrParmValue::getFloat)
.equalAsObjects(AttrParmValue::getInt)
.equalAsObjects(AttrParmValue::getStr)
.equalAsObjects(AttrParm::getValue)
.conclusion;
}

Expand Down
20 changes: 1 addition & 19 deletions org.lflang/src/org/lflang/ast/ToLf.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.lflang.lf.ArraySpec;
import org.lflang.lf.Assignment;
import org.lflang.lf.AttrParm;
import org.lflang.lf.AttrParmValue;
import org.lflang.lf.Attribute;
import org.lflang.lf.BuiltinTriggerRef;
import org.lflang.lf.Code;
Expand Down Expand Up @@ -288,24 +287,7 @@ public MalleableString caseAttrParm(AttrParm object) {
// (name=ID '=')? value=AttrParmValue;
var builder = new Builder();
if (object.getName() != null) builder.append(object.getName()).append(" = ");
return builder.append(doSwitch(object.getValue())).get();
}

@Override
public MalleableString caseAttrParmValue(AttrParmValue object) {
// str=STRING
// | int=SignedInt
// | bool=Boolean
// | float=SignedFloat
if (object.getStr() != null) {
return MalleableString.anyOf(StringUtil.addDoubleQuotes(object.getStr()));
}
if (object.getInt() != null) return MalleableString.anyOf(object.getInt());
if (object.getBool() != null) return MalleableString.anyOf(object.getBool());
if (object.getFloat() != null) return MalleableString.anyOf(object.getFloat());
throw new IllegalArgumentException(
"The attributes of an AttrParmValue should not all be null."
);
return builder.append(object.getValue()).get();
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion org.lflang/src/org/lflang/generator/rust/RustModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ object RustModelBuilder {
body = n.code.toText(),
isStartup = n.triggers.any { it is BuiltinTriggerRef && it.type == BuiltinTrigger.STARTUP },
isShutdown = n.triggers.any { it is BuiltinTriggerRef && it.type == BuiltinTrigger.SHUTDOWN },
debugLabel = AttributeUtils.label(n),
debugLabel = AttributeUtils.getLabel(n),
loc = n.locationInfo().let {
// remove code block
it.copy(lfText = it.lfText.replace(TARGET_BLOCK_R, "{= ... =}"))
Expand Down
Loading