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

Annotations #977

Merged
merged 24 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8bb0e06
Extend syntax
lsk567 Feb 23, 2022
861ac63
Extend syntax, update diagram synthesis, update validator
lsk567 Feb 23, 2022
82fdef1
Merge branch 'master' into attribute
oowekyala Jun 8, 2022
605c3f9
Add annotation validation
oowekyala Jun 8, 2022
39f22de
Support shorthand syntax
oowekyala Jun 8, 2022
f047cc8
add another test
oowekyala Jun 8, 2022
9d15a71
Support trailing comma
oowekyala Jun 8, 2022
505efe6
Support attributes on more constructs
oowekyala Jun 9, 2022
12ec7d5
Fix diagram synthesis
oowekyala Jun 9, 2022
bb74cf3
Resolve conflicts with master
lhstrh Jun 18, 2022
af7502e
Fix remaining conflict
lhstrh Jun 18, 2022
bed6940
Re-organize attributes in the LFValidator
lsk567 Jun 22, 2022
080e5f8
Add comments
lsk567 Jun 22, 2022
c30a7c9
Support basic param types such as String, Int, Boolean, and Float.
lsk567 Jun 23, 2022
14c7ec2
Add a unit test that checks whether the validator can detect wrong pa…
lsk567 Jun 23, 2022
d261dc4
Merge remote-tracking branch 'origin/master' into attribute
lsk567 Jun 23, 2022
8b944e5
Remove the dependency on NotNull
lsk567 Jun 23, 2022
d515475
Factor out AttributeSpec
lsk567 Jun 23, 2022
9b38e25
Apply suggestions from code review
lsk567 Jun 23, 2022
b33484b
Add author tags
lsk567 Jun 23, 2022
5de2169
Move the static block and static map back to AttributeSpec
lsk567 Jun 23, 2022
b24e5b5
Change the visibility of the overridden error() back to protected.
lsk567 Jun 23, 2022
8b0d893
Apply quick fix suggested by @lhstrh to remove ambiguity
lsk567 Jun 25, 2022
c92516e
Factor out attribute-related methods into their own AttributeUtils class
lsk567 Jun 25, 2022
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 @@ -1365,7 +1365,7 @@ private KNode addErrorComment(KNode node, String message) {

private Iterable<KNode> createUserComments(EObject element, KNode targetNode) {
if (getBooleanValue(SHOW_USER_LABELS)) {
String commentText = ASTUtils.findAnnotationInComments(element, "@label");
String commentText = ASTUtils.label(element);

if (!StringExtensions.isNullOrEmpty(commentText)) {
KNode comment = _kNodeExtensions.createNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.testing.extensions.InjectionExtension;
import org.eclipse.xtext.testing.util.ParseHelper;

import org.lflang.lf.LfPackage;
import org.lflang.lf.Model;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -65,4 +67,50 @@ public void checkForTarget() throws Exception {
Assertions.assertNotNull(result);
Assertions.assertFalse(result.eResource().getErrors().isEmpty(), "Failed to catch misspelled target keyword.");
}
}

@Test
public void testAttributes() throws Exception {
String testCase = """
target C;
@label("somethign", "else")
@ohio()
@a
@bdebd(a="b")
@bd("abc")
@bd("abc",)
@a(a="a", b="b")
@a(a="a", b="b",)
main reactor {

}
""";
parseWithoutError(testCase);
}

@Test
public void testAttributeContexts() throws Exception {
String testCase = """
target C;
@a
main reactor(@b parm: int) {

@ohio reaction() {==}
@ohio logical action f;
@ohio timer t;
@ohio input q: int;
@ohio output q2: int;
}
""";
parseWithoutError(testCase);
}

private Model parseWithoutError(String s) throws Exception {
Model model = parser.parse(s);
Assertions.assertNotNull(model);
Assertions.assertTrue(model.eResource().getErrors().isEmpty(),
"Encountered unexpected error while parsing: " +
model.eResource().getErrors());
return model;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,30 @@ public void testUnrecognizedTarget() throws Exception {
"Unrecognized target: Pjthon");
}


@Test
public void testWrongStructureForLabelAttribute() throws Exception {
String testCase = """
target C;
@label(name="something")
main reactor { }
""";
validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getAttribute(), null,
"Unknown attribute parameter.");
}


@Test
public void testMissingName() throws Exception {
String testCase = """
target C;
@label("something", "else")
main reactor { }
""";
validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getAttribute(), null,
"Missing name for attribute parameter.");
}

@Test
public void testInitialMode() throws Exception {
String testCase = """
Expand Down
64 changes: 61 additions & 3 deletions org.lflang/src/org/lflang/ASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.Assignment;
import org.lflang.lf.Attribute;
import org.lflang.lf.Code;
import org.lflang.lf.Connection;
import org.lflang.lf.Element;
Expand Down Expand Up @@ -1747,16 +1748,73 @@ public static String findAnnotationInComments(EObject object, String key) {
return null;
}

/**
* Return the value of the {@code @label} attribute if
* present, otherwise return null.
*
* @throws IllegalArgumentException If the node cannot have attributes
*/
public static String findLabelAttribute(EObject node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense if this was in a separate class like AttributeUtils? Same for other stuff here that relate to attributes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I like this idea.

List<Attribute> attrs = getAttributes(node);
return attrs.stream()
.filter(it -> it.getAttrName().equals("label"))
.map(it -> it.getAttrParms().get(0).getValue())
.findFirst()
.orElse(null);

}

/**
* Return the attributes declared on the given node. Throws
* if the node does not support declaring attributes.
*
* @throws IllegalArgumentException If the node cannot have attributes
*/
public static List<Attribute> getAttributes(EObject node) {
if (node instanceof Reactor) {
return ((Reactor) node).getAttributes();
} else if (node instanceof Reaction) {
return ((Reaction) node).getAttributes();
} else if (node instanceof Action) {
return ((Action) node).getAttributes();
} else if (node instanceof Timer) {
return ((Timer) node).getAttributes();
} else if (node instanceof StateVar) {
return ((StateVar) node).getAttributes();
} else if (node instanceof Parameter) {
return ((Parameter) node).getAttributes();
} else if (node instanceof Input) {
return ((Input) node).getAttributes();
} else if (node instanceof Output) {
return ((Output) node).getAttributes();
}
throw new IllegalArgumentException("Not annotatable: " + node);
}

/**
* 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.
*/
public static String label(Reaction n) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that this overload could be removed if binary compatibility is not important, which seems likely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't get this comment. What binary compatibility?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone has compiled a class that depends on this method and we remove it, their class will break. Minor releases generally preserve binary compatibility. I'm not sure whether this java API is publicly supported since you've started public releases... If the only ide extensions that use the java API are in this repo then we can most likely remove it (we're also pre-1.0.0)

return findAnnotationInComments(n, "@label");
return label((EObject) n);
}


/**
* 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
*/
public static String label(EObject n) {
String fromAttr = findLabelAttribute(n);
if (fromAttr == null) {
return findAnnotationInComments(n, "@label");
}
return fromAttr;
}

/**
* Find the main reactor and set its name if none was defined.
* @param resource The resource to find the main reactor in.
Expand Down
30 changes: 23 additions & 7 deletions org.lflang/src/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ ImportedReactor: reactorClass=[Reactor] ('as' name=ID)?;
* Declaration of a reactor class.
*/
Reactor:
{Reactor} ((federated?='federated' | main?='main')? & realtime?='realtime'?) 'reactor' (name=ID)?
{Reactor} (attributes+=Attribute)* ((federated?='federated' | main?='main')? & realtime?='realtime'?) 'reactor' (name=ID)?
('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')?
('(' parameters+=Parameter (',' parameters+=Parameter)* ')')?
('at' host=Host)?
Expand Down Expand Up @@ -129,6 +129,7 @@ TargetDecl:
* must be given, or a literal or code that denotes zero.
*/
StateVar:
(attributes+=Attribute)*
(reset?='reset')? 'state' name=ID (
(':' (type=Type))?
((parens+='(' (init+=Expression (',' init+=Expression)*)? parens+=')')
Expand All @@ -150,24 +151,24 @@ MethodArgument:
;

Input:
mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?;
(attributes+=Attribute)* mutable?='mutable'? 'input' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?;

Output:
'output' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?;
(attributes+=Attribute)* 'output' (widthSpec=WidthSpec)? name=ID (':' type=Type)? ';'?;

// Timing specification for a timer: (offset, period)
// Can be empty, which means (0,0) = (NOW, ONCE).
// E.g. (0) or (NOW) or (NOW, ONCE) or (100, 1000)
// The latter means fire with period 1000, offset 100.
Timer:
'timer' name=ID ('(' offset=Expression (',' period=Expression)? ')')? ';'?;
(attributes+=Attribute)* 'timer' name=ID ('(' offset=Expression (',' period=Expression)? ')')? ';'?;

Boolean:
TRUE | FALSE
;

Mode:
{Mode} (initial?='initial')? 'mode' (name=ID)?
{Mode} (initial?='initial')? 'mode' (name=ID)?
'{' (
(stateVars+=StateVar) |
(timers+=Timer) |
Expand All @@ -188,11 +189,13 @@ Mode:
// For all actions, minSpacing is the minimum difference between
// the tags of two subsequently scheduled events.
Action:
(origin=ActionOrigin)? 'action' name=ID
(attributes+=Attribute)*
(origin=ActionOrigin)? 'action' name=ID
('(' minDelay=Expression (',' minSpacing=Expression (',' policy=STRING)? )? ')')?
(':' type=Type)? ';'?;

Reaction:
(attributes+=Attribute)*
('reaction')
('(' (triggers+=TriggerRef (',' triggers+=TriggerRef)*)? ')')?
(sources+=VarRef (',' sources+=VarRef)*)?
Expand Down Expand Up @@ -244,6 +247,18 @@ Serializer:
'serializer' type=STRING
;

/////////// Attributes
Attribute:
'@' attrName=ID ('(' (attrParms+=AttrParm (',' attrParms+=AttrParm)* ','?)? ')')?
;

AttrParm:
(name=ID '=')? value=AttrParmValue;
oowekyala marked this conversation as resolved.
Show resolved Hide resolved

AttrParmValue:
STRING
;

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

KeyValuePairs:
Expand Down Expand Up @@ -289,6 +304,7 @@ Assignment:
* Parameter declaration with optional type and mandatory initialization.
*/
Parameter:
(attributes+=Attribute)*
name=ID (':' (type=Type))?
((parens+='(' (init+=Expression (',' init+=Expression)*)? parens+=')')
| (braces+='{' (init+=Expression (',' init+=Expression)*)? braces+='}')
Expand Down Expand Up @@ -510,4 +526,4 @@ Token:
'@' |
// Single quotes
"'"
;
;
Loading