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

Angular bracket imports for reusable reactor modules #2404

Merged
merged 36 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f58ee50
Added angular bracket import for reusable reactors module
vinzbarbuto Sep 9, 2024
4319ead
Added angular bracket import for reusable reactors module
vinzbarbuto Sep 9, 2024
526ee72
Merge with 'origin/extending'
vinzbarbuto Sep 9, 2024
e6f4cbc
Update core/src/main/java/org/lflang/ast/ToSExpr.java
lhstrh Sep 9, 2024
c25b2f3
Fix bug in ToLf.java; Added test for new import statement
vinzbarbuto Sep 10, 2024
6576267
Fix bug in the LinguaFranca.text file
vinzbarbuto Sep 10, 2024
781a552
Extend support for new import statements in federated LF programs; Ad…
vinzbarbuto Sep 11, 2024
d8c0ac6
Add the new terminal LIB_PATH to Token
vinzbarbuto Sep 11, 2024
88070a2
Add the new terminal LIB_PATH to Token
vinzbarbuto Sep 11, 2024
6dafb85
Resolved conflict in LunguaFranca.xtext by removing the LIB_PATH term…
vinzbarbuto Sep 16, 2024
5c8dd4e
Corrected formatting and updated import statements for generated file…
vinzbarbuto Sep 16, 2024
e3ce6cf
Solved formatting error
vinzbarbuto Sep 18, 2024
f05c567
Removed quotes in setImportPackage
vinzbarbuto Sep 18, 2024
ab4391b
Fix format
vinzbarbuto Sep 18, 2024
c36bf05
Enhanced tests for new import statement
vinzbarbuto Sep 18, 2024
3da89fc
Enhanced tests for new import statement; Fix formatting
vinzbarbuto Sep 18, 2024
9af3158
Added angular bracket import for reusable reactors module
vinzbarbuto Sep 9, 2024
5ec9422
Update core/src/main/java/org/lflang/ast/ToSExpr.java
lhstrh Sep 9, 2024
95366ab
Fix bug in ToLf.java; Added test for new import statement
vinzbarbuto Sep 10, 2024
aab992d
Fix bug in the LinguaFranca.text file
vinzbarbuto Sep 10, 2024
fabf6b5
Extend support for new import statements in federated LF programs; Ad…
vinzbarbuto Sep 11, 2024
031ac4e
Add the new terminal LIB_PATH to Token
vinzbarbuto Sep 11, 2024
82ccc7f
Add the new terminal LIB_PATH to Token
vinzbarbuto Sep 11, 2024
5ac1d15
Resolved conflict in LunguaFranca.xtext by removing the LIB_PATH term…
vinzbarbuto Sep 16, 2024
52cfaf6
Corrected formatting and updated import statements for generated file…
vinzbarbuto Sep 16, 2024
fe878a5
Solved formatting error
vinzbarbuto Sep 18, 2024
3dadae8
Removed quotes in setImportPackage
vinzbarbuto Sep 18, 2024
6345732
Fix format
vinzbarbuto Sep 18, 2024
feee84b
Enhanced tests for new import statement
vinzbarbuto Sep 18, 2024
338726e
Enhanced tests for new import statement; Fix formatting
vinzbarbuto Sep 18, 2024
40ddf3b
Merge branch 'extending' of https://github.com/lf-lang/lingua-franca …
vinzbarbuto Sep 23, 2024
67be868
Added full support for the new import statement structure for Lingo l…
vinzbarbuto Sep 24, 2024
0b94151
Merge branch 'master' into extending
vinzbarbuto Sep 24, 2024
a32c7ed
Enhance path handling in ImportUtil.java and add new test
vinzbarbuto Sep 30, 2024
d5bdd9a
Change ImportUtil.buildPackageURIfromSrc to return a Path object inst…
vinzbarbuto Sep 30, 2024
6427abb
Changed path in ImportUtil from 'target/lfc_include' to 'build/lfc_in…
vinzbarbuto Oct 1, 2024
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,6 @@ gradle-app.setting
*.jar
core/model/

# Exclude all build directories except test/Python/build for testing purposes
!test/Python/build/

13 changes: 12 additions & 1 deletion core/src/main/java/org/lflang/LFResourceDescriptionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.eclipse.xtext.scoping.impl.ImportUriResolver;
import org.eclipse.xtext.util.IAcceptor;
import org.lflang.lf.Model;
import org.lflang.util.ImportUtil;

/**
* Resource description strategy designed to limit global scope to only those files that were
Expand Down Expand Up @@ -77,7 +78,17 @@ public boolean createEObjectDescriptions(
*/
private void createEObjectDescriptionForModel(
Model model, IAcceptor<IEObjectDescription> acceptor) {
var uris = model.getImports().stream().map(uriResolver).collect(Collectors.joining(DELIMITER));
var uris =
model.getImports().stream()
.map(
importObj -> {
return (importObj.getImportURI() != null)
? importObj.getImportURI()
: ImportUtil.buildPackageURI(
importObj.getImportPackage(),
model.eResource()); // Use the resolved import string
})
.collect(Collectors.joining(DELIMITER));
var userData = Map.of(INCLUDES, uris);
QualifiedName qname = QualifiedName.create(model.eResource().getURI().toString());
acceptor.accept(EObjectDescription.create(qname, model, userData));
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/lflang/LinguaFranca.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Model:
/**
* Import declaration.
*/
Import: 'import' reactorClasses+=ImportedReactor (',' reactorClasses+=ImportedReactor)* 'from' importURI=STRING ';'?;
Import: 'import' reactorClasses+=ImportedReactor (',' reactorClasses+=ImportedReactor)* 'from' (importURI=STRING | '<' importPackage=Path '>') ';'?;
lhstrh marked this conversation as resolved.
Show resolved Hide resolved

ReactorDecl: Reactor | ImportedReactor;

Expand Down Expand Up @@ -478,7 +478,7 @@ Code:
;

FSName:
(ID | '.' | '_')+
(ID | '.' | '_' | '-')+
;
// Absolute or relative directory path in Windows, Linux, or MacOS.
Path:
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/lflang/ast/IsEqual.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public Boolean caseModel(Model object) {
public Boolean caseImport(Import object) {
return new ComparisonMachine<>(object, Import.class)
.equalAsObjects(Import::getImportURI)
.equalAsObjects(Import::getImportPackage)
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
.listsEquivalent(Import::getReactorClasses)
.conclusion;
}
Expand Down
8 changes: 5 additions & 3 deletions core/src/main/java/org/lflang/ast/ToLf.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,11 @@ public MalleableString caseImport(Import object) {
.append("import ")
// TODO: This is a place where we can use conditional parentheses.
.append(list(", ", "", "", false, true, true, object.getReactorClasses()))
.append(" from \"")
.append(object.getImportURI())
.append("\"")
.append(" from ")
.append(
object.getImportURI() != null
? "\"" + object.getImportURI() + "\""
: "<" + object.getImportPackage() + ">")
.get();
}

Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/org/lflang/ast/ToSExpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ public SExpr caseImport(Import object) {
// reactorClasses+=ImportedReactor)* 'from' importURI=STRING ';'?;
return sList(
"import",
new SAtom<>(object.getImportURI()),
new SAtom<>(
object.getImportURI() != null ? object.getImportURI() : object.getImportPackage()),
sList("reactors", object.getReactorClasses()));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.lflang.federated.generator;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -9,6 +10,7 @@
import org.lflang.generator.CodeBuilder;
import org.lflang.lf.Import;
import org.lflang.lf.Model;
import org.lflang.util.ImportUtil;

/**
* Helper class to generate import statements for a federate.
Expand All @@ -31,7 +33,15 @@ String generateImports(FederateInstance federate, FederationFileConfig fileConfi
.forEach(
i -> {
visitedImports.add(i);
Path importPath = fileConfig.srcPath.resolve(i.getImportURI()).toAbsolutePath();
Path importPath =
fileConfig
.srcPath
.resolve(
i.getImportURI() != null
? Paths.get(i.getImportURI())
: ImportUtil.buildPackageURIfromSrc(
i.getImportPackage(), fileConfig.srcPath.toString()))
.toAbsolutePath();
i.setImportURI(
fileConfig.getSrcPath().relativize(importPath).toString().replace('\\', '/'));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
/**
* Global scope provider that limits access to only those files that were explicitly imported.
*
* <p>Adapted from from Xtext manual, Chapter 8.7.
* <p>Adapted from Xtext manual, Chapter 8.7.
*
* @author Marten Lohstroh
* @see <a href="https://www.eclipse.org/Xtext/documentation/2.6.0/Xtext%20Documentation.pdf">xtext
Expand Down
15 changes: 13 additions & 2 deletions core/src/main/java/org/lflang/scoping/LFScopeProviderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@
package org.lflang.scoping;

import static java.util.Collections.emptyList;
import static org.lflang.ast.ASTUtils.*;
import static org.lflang.ast.ASTUtils.allActions;
import static org.lflang.ast.ASTUtils.allInputs;
import static org.lflang.ast.ASTUtils.allOutputs;
import static org.lflang.ast.ASTUtils.allParameters;
import static org.lflang.ast.ASTUtils.allTimers;
import static org.lflang.ast.ASTUtils.allWatchdogs;
import static org.lflang.ast.ASTUtils.toDefinition;

import com.google.inject.Inject;
import java.util.ArrayList;
Expand All @@ -50,6 +56,7 @@
import org.lflang.lf.ReactorDecl;
import org.lflang.lf.VarRef;
import org.lflang.lf.Watchdog;
import org.lflang.util.ImportUtil;

/**
* This class enforces custom rules. In particular, it resolves references to parameters, ports,
Expand Down Expand Up @@ -104,7 +111,11 @@ public IScope getScope(EObject context, EReference reference) {
* statement.
*/
protected IScope getScopeForImportedReactor(ImportedReactor context, EReference reference) {
String importURI = ((Import) context.eContainer()).getImportURI();
String importURI =
((Import) context.eContainer()).getImportURI() != null
? ((Import) context.eContainer()).getImportURI()
: ImportUtil.buildPackageURI(
((Import) context.eContainer()).getImportPackage(), context.eResource());
var importedURI =
scopeProvider.resolve(importURI == null ? "" : importURI, context.eResource());
if (importedURI != null) {
Expand Down
107 changes: 107 additions & 0 deletions core/src/main/java/org/lflang/util/ImportUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.lflang.util;

import java.nio.file.Path;
import java.nio.file.Paths;
import org.eclipse.emf.ecore.resource.Resource;

/**
* Utility class for handling package-related URIs in the context of LF (Lingua Franca) libraries.
* This class provides methods to build URIs for accessing library files based on their location in
* a project structure, specifically targeting the "build/lfc_include" directory for library
* inclusion.
*/
public class ImportUtil {

/**
* Builds a package URI based on the provided URI string and resource. It traverses upwards from
* the current resource URI until it finds the "src" directory, then constructs the final URI
* pointing to the library file within the "build/lfc_include" directory.
*
* @param uriStr A string representing the URI of the file. It must contain both the library name
* and file name, separated by a '/'.
* @param resource The resource from which the URI resolution should start.
* @return The constructed package URI as a string.
* @throws IllegalArgumentException if the URI string does not contain both library and file
* names.
*/
public static String buildPackageURI(String uriStr, Resource resource) {
Path rootPath = Paths.get(resource.getURI().toString()).toAbsolutePath();

Path uriPath = Paths.get(uriStr.trim());

if (uriPath.getNameCount() < 2) {
throw new IllegalArgumentException("URI must contain both library name and file name.");
}

// Initialize the path as the current directory
Path finalPath = Paths.get("");

// Traverse upwards until we reach the "src/" directory
while (!rootPath.endsWith("src")) {
rootPath = rootPath.getParent();
if (rootPath == null) {
throw new IllegalArgumentException("The 'src' directory was not found in the given path.");
}
finalPath = finalPath.resolve("..");
}

// Build the final path
finalPath =
finalPath
.resolve("build")
.resolve("lfc_include")
.resolve(uriPath.getName(0))
.resolve("src")
.resolve("lib")
.resolve(uriPath.getName(1));

return finalPath.toString();
}

/**
* Builds a package URI based on the provided URI string and source path. This method works
* similarly to the `buildPackageURI`, but it accepts a direct source path instead of a resource.
* It traverses upwards to locate the "src/" directory and then constructs the URI pointing to the
* library file.
*
* @param uriStr A string representing the URI of the file. It must contain both the library name
* and file name, separated by a '/'.
* @param root The root path from which the URI resolution should start.
* @return The constructed package URI as a string.
* @throws IllegalArgumentException if the URI string or source path is null, empty, or does not
* contain both the library name and file name.
*/
public static Path buildPackageURIfromSrc(String uriStr, String root) {
if (uriStr == null || root == null || uriStr.trim().isEmpty() || root.trim().isEmpty()) {
throw new IllegalArgumentException("URI string and source path must not be null or empty.");
}

Path uriPath = Paths.get(uriStr.trim());

if (uriPath.getNameCount() < 2) {
throw new IllegalArgumentException("URI must contain both library name and file name.");
}

// Use the src path to create a base path
Path rootPath = Paths.get(root).toAbsolutePath();

// Traverse upwards until we reach the "src/" directory
while (!rootPath.endsWith("src")) {
rootPath = rootPath.getParent();
if (rootPath == null) {
throw new IllegalArgumentException("The 'src' directory was not found in the given path.");
}
}

Path finalPath =
rootPath
.resolveSibling("build")
.resolve("lfc_include")
.resolve(uriPath.getName(0)) // library name
.resolve("src")
.resolve("lib")
.resolve(uriPath.getName(1)); // file name

return finalPath;
}
}
12 changes: 12 additions & 0 deletions test/Python/build/lfc_include/library-test/src/lib/Import.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
target Python

reactor Count(offset=0, period = 1 sec) {
state count = 1
output out
timer t(offset, period)

reaction(t) -> out {=
out.set(self.count)
self.count += 1
=}
}
21 changes: 21 additions & 0 deletions test/Python/src/LingoFederatedImport.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Test the new import statement for Lingo downloaded packages with the import path enclosed in angle brackets
# Version 1: The LF file is located in "src".
target Python {
timeout: 2 sec
}

import Count from <library-test/Import.lf>

reactor Actuator {
input results

reaction(results) {=
print(f"Count: {results.value}")
=}
}

federated reactor {
count = new Count()
act = new Actuator()
count.out -> act.results
}
21 changes: 21 additions & 0 deletions test/Python/src/lingo_imports/FederatedTestImportPackages.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Test the new import statement for Lingo downloaded packages with the import path enclosed in angle brackets
# Version 2: The LF file is now located in a subdirectory under "src".
target Python {
timeout: 2 sec
}

import Count from <library-test/Import.lf>

reactor Actuator {
input results

reaction(results) {=
print(f"Count: {results.value}")
=}
}

federated reactor {
count = new Count()
act = new Actuator()
count.out -> act.results
}
20 changes: 20 additions & 0 deletions test/Python/src/lingo_imports/TestImportPackages.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Test the new import statement for Lingo downloaded packages with the import path enclosed in angle brackets
target Python {
timeout: 2 sec
}

import Count from <library-test/Import.lf>

reactor Actuator {
input results

reaction(results) {=
print(f"Count: {results.value}")
=}
}

main reactor {
count = new Count()
act = new Actuator()
count.out -> act.results
}
Loading