From 8d3e2019366aa6df2edf4c3c60126f920bc8e651 Mon Sep 17 00:00:00 2001 From: Johannes Mey <johannes.mey@tu-dresden.de> Date: Fri, 3 Jan 2020 19:31:35 +0100 Subject: [PATCH] initial version of modelica scope tree generation. inheritance not treated properly yet. --- scope4m/src/main/jastadd/Helpers.jadd | 22 ++++--- .../src/main/jastadd/ModelicaToScopeTree.jrag | 63 ++++++++++++++++++- .../main/jastadd/ModelicaToScopeTree.relast | 18 ++++++ .../java/org/jmodelica/ScopeAnalysis.java | 9 ++- .../org/jmodelica/ComplicatedScopeTest.java | 20 ++++++ .../java/org/jmodelica/EncapsulationTest.java | 16 +++++ .../java/org/jmodelica/ScopeAnalysisTest.java | 36 +++++++++++ .../java/org/jmodelica/SimpleScopeTest.java | 20 ++++++ .../test/resources/complicated/complicated.mo | 0 .../resources/encapsulated/encapsulation.mo | 19 ++++++ scope4m/src/test/resources/simple/simple.mo | 17 +++++ testprograms/modelica/simple/simple.mo | 17 +++++ 12 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 scope4m/src/test/java/org/jmodelica/ComplicatedScopeTest.java create mode 100644 scope4m/src/test/java/org/jmodelica/EncapsulationTest.java create mode 100644 scope4m/src/test/java/org/jmodelica/ScopeAnalysisTest.java create mode 100644 scope4m/src/test/java/org/jmodelica/SimpleScopeTest.java rename testprograms/modelica/ComplicatedNameLookup.mo => scope4m/src/test/resources/complicated/complicated.mo (100%) create mode 100644 scope4m/src/test/resources/encapsulated/encapsulation.mo create mode 100644 scope4m/src/test/resources/simple/simple.mo create mode 100644 testprograms/modelica/simple/simple.mo diff --git a/scope4m/src/main/jastadd/Helpers.jadd b/scope4m/src/main/jastadd/Helpers.jadd index 3e171a6..76a6d97 100644 --- a/scope4m/src/main/jastadd/Helpers.jadd +++ b/scope4m/src/main/jastadd/Helpers.jadd @@ -1,9 +1,7 @@ -/** copy of the extendj source location interface */ +/** helper methods to provide source locations */ aspect SourceLocation { - protected String ASTNode.sourceFile() { - return retrieveFileName(); - } + syn String ASTNode.sourceLocation() = sourceFile() + ":" + lineNumber(); syn int ASTNode.lineNumber() { ASTNode n = this; @@ -12,8 +10,18 @@ aspect SourceLocation { } return getLine(n.getStart()); } + eq ClassDeclScope.lineNumber() = getClassDecl().lineNumber(); + eq ForStmtScope.lineNumber() = getForStmt().lineNumber(); + eq ComponentDeclaration.lineNumber() = getComponentDecl().lineNumber(); - public String ASTNode.sourceLocation() { - return sourceFile() + ":" + lineNumber(); - } + eq ClassDeclScope.toString() = "Class-" + getClassDecl().name() + "-Scope"; + eq ForStmtScope.toString() = getForStmt().getClass().getSimpleName() + "-Scope"; + + eq ComponentDeclaration.toString() = getComponentDecl().getClass().getSimpleName() + ":" + super.toString(); + + syn String ASTNode.sourceFile() = "<unknown>"; + + eq ClassDeclScope.sourceFile() = getClassDecl().sourceFile(); + eq ForStmtScope.sourceFile() = getForStmt().sourceFile(); + eq ComponentDeclaration.sourceFile() = getComponentDecl().sourceFile(); } diff --git a/scope4m/src/main/jastadd/ModelicaToScopeTree.jrag b/scope4m/src/main/jastadd/ModelicaToScopeTree.jrag index b3c375d..d9a6b42 100644 --- a/scope4m/src/main/jastadd/ModelicaToScopeTree.jrag +++ b/scope4m/src/main/jastadd/ModelicaToScopeTree.jrag @@ -1,12 +1,71 @@ aspect ModelicaToScopeTree { - syn lazy ScopeTree SourceRoot.scopeTree() { + /** a relational nta collection attribute to compute the scope tree */ + syn lazy ScopeTree SourceRoot.scopeTree() = (ScopeTree) scope(); + + /** a relational nta collection attribute to compute scopes */ + coll Scope ASTNode.scope() [asScope()] with addElement root SourceRoot; + + // collect all scopes + SrcClassDecl contributes scope() when !isEncapsulated() to ASTNode.scope() for containingScope(); + // if a scope is encapsulated, it is added to the top-level, because the inner + SrcClassDecl contributes scope() when isEncapsulated() to ASTNode.scope() for srcRoot(); + + SrcForStmt contributes scope() to ASTNode.scope() for containingScope(); + + // collect all elements + SrcComponentDecl contributes asDeclaration() to ASTNode.scope() for containingScope(); +} + +/** + * ascpect containing helper methods to construct (mostly empty) AST nodes of the scope tree + * If it was not for the single line in asProtectedScope(), the rest of this aspect could have been generated + * automatically, which would have been much nicer! + */ +aspect ScopeTreeConstructors { + + syn lazy ScopeTree SourceRoot.asScope() { ScopeTree tree = new ScopeTree(); tree.setSourceRoot(this); - return tree; } + + /** fallback attribute to ensure every AST element could pontentially be a scope */ + syn Scope ASTNode.asScope() { + throw new RuntimeException("unable to create a scope for type " + getClass().getSimpleName()); + } + + syn lazy ClassDeclScope SrcClassDecl.asScope() { + ClassDeclScope scope = new ClassDeclScope(); + scope.setClassDecl(this); + return scope; + } + + syn lazy ForStmtScope SrcForStmt.asScope() { + ForStmtScope scope = new ForStmtScope(); + scope.setForStmt(this); + return scope; + } + + syn lazy ComponentDeclaration SrcComponentDecl.asDeclaration() { + ComponentDeclaration decl = new ComponentDeclaration(getName().getID()); + decl.setComponentDecl(this); + return decl; + } + } aspect ScopeGenerationAttributes { + /** determine the scope an AST element is contained in or belongs to.*/ + inh lazy ASTNode SrcClassDecl.containingScope(); + inh lazy ASTNode SrcForStmt.containingScope(); + inh lazy ASTNode SrcComponentDecl.containingScope(); + // contained in scope: + eq SourceRoot.getChild().containingScope() = this; + eq SrcClassDecl.getChild().containingScope() = this; + eq SrcForStmt.getChild().containingScope() = this; + + inh SourceRoot SrcBaseNode.srcRoot(); + eq SourceRoot.getChild().srcRoot() = this; + } diff --git a/scope4m/src/main/jastadd/ModelicaToScopeTree.relast b/scope4m/src/main/jastadd/ModelicaToScopeTree.relast index 4c47dbc..082b97d 100644 --- a/scope4m/src/main/jastadd/ModelicaToScopeTree.relast +++ b/scope4m/src/main/jastadd/ModelicaToScopeTree.relast @@ -1,2 +1,20 @@ // glue relation for the Java-based variable shadowing analysis rel ScopeTree.SourceRoot -> SourceRoot; + +// scopes + +ClassDeclScope : Scope; +rel ClassDeclScope.classDecl -> SrcClassDecl; + +ForStmtScope : Scope; +rel ForStmtScope.forStmt -> SrcForStmt; + +// we do not need the for iter expression, because it cannot contains declarations +// IterExpScope : Scope; +// rel IterExpScope.iterExp -> SrcIterExp; + +// declarations + +ComponentDeclaration : Declaration; +rel ComponentDeclaration.componentDecl -> SrcComponentDecl; + diff --git a/scope4m/src/main/java/org/jmodelica/ScopeAnalysis.java b/scope4m/src/main/java/org/jmodelica/ScopeAnalysis.java index f7ce25d..a7dd42d 100644 --- a/scope4m/src/main/java/org/jmodelica/ScopeAnalysis.java +++ b/scope4m/src/main/java/org/jmodelica/ScopeAnalysis.java @@ -37,10 +37,10 @@ public class ScopeAnalysis { boolean warnings = arguments.remove("--warnings"); if (arguments.size() > 1) { - System.out.println("usage: ScopeAnalysis [--debug] [--tree] [--warnings] <directory with java files>"); + System.out.println("usage: ScopeAnalysis [--debug] [--tree] [--warnings] <directory with modelica files>"); System.exit(-1); } - String path = arguments.isEmpty() ? "../testprograms/modelica" : arguments.get(arguments.size() - 1); + String path = arguments.isEmpty() ? "../testprograms/modelica/simple" : arguments.get(arguments.size() - 1); if (debug) { new ScopeAnalysis().analyze(path, tree, warnings); @@ -117,6 +117,7 @@ public class ScopeAnalysis { if (warnings) { // TODO find out if there are compiler warnings in JModelica + System.out.println("Currently, compiler warnings are not supported for jModelica."); } Set<AbstractFinding> findings = scopeTree.variableShadowings(); @@ -129,15 +130,13 @@ public class ScopeAnalysis { return findings; } catch (IOException | Parser.Exception | CompilerException e) { + System.out.println("Current relative path is: " + Paths.get("").toAbsolutePath()); throw new RuntimeException(e); } } private static SourceRoot readProgram(Collection<String> files) throws IOException, beaver.Parser.Exception, CompilerException { - - ModelicaCompiler mc = new ModelicaCompiler(ModelicaCompiler.createOptions()); return mc.getParserHandler().parseModel(UtilInterface.create(mc), (files.toArray(new String[]{}))); - } } diff --git a/scope4m/src/test/java/org/jmodelica/ComplicatedScopeTest.java b/scope4m/src/test/java/org/jmodelica/ComplicatedScopeTest.java new file mode 100644 index 0000000..77496a1 --- /dev/null +++ b/scope4m/src/test/java/org/jmodelica/ComplicatedScopeTest.java @@ -0,0 +1,20 @@ +package org.jmodelica; + +import org.jmodelica.compiler.AbstractFinding; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +public class ComplicatedScopeTest extends ScopeAnalysisTest { + + @Test + void test() { + + ScopeAnalysis scopeAnalysis = new ScopeAnalysis(); + Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/complicated/", true, false); + + + } + +} diff --git a/scope4m/src/test/java/org/jmodelica/EncapsulationTest.java b/scope4m/src/test/java/org/jmodelica/EncapsulationTest.java new file mode 100644 index 0000000..065a38d --- /dev/null +++ b/scope4m/src/test/java/org/jmodelica/EncapsulationTest.java @@ -0,0 +1,16 @@ +package org.jmodelica; + +import org.jmodelica.compiler.AbstractFinding; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +public class EncapsulationTest extends ScopeAnalysisTest { + + @Test + void test() { + ScopeAnalysis scopeAnalysis = new ScopeAnalysis(); + Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/encapsulated", true, false); + } + +} diff --git a/scope4m/src/test/java/org/jmodelica/ScopeAnalysisTest.java b/scope4m/src/test/java/org/jmodelica/ScopeAnalysisTest.java new file mode 100644 index 0000000..e097ec4 --- /dev/null +++ b/scope4m/src/test/java/org/jmodelica/ScopeAnalysisTest.java @@ -0,0 +1,36 @@ +package org.jmodelica; + +import org.jmodelica.compiler.AbstractFinding; +import org.jmodelica.compiler.Declaration; +import org.jmodelica.compiler.MultipleDeclarationFinding; +import org.jmodelica.compiler.VariableShadowFinding; +import org.junit.jupiter.api.Assertions; + +import java.util.Set; + +public abstract class ScopeAnalysisTest { + static void assertShadow(Set<AbstractFinding> findings, String name, int shadowerLine, int shadowedLine) { + for (AbstractFinding finding : findings) { + if (finding instanceof VariableShadowFinding) { + Declaration shadower = ((VariableShadowFinding)finding).getShadower(); + Declaration shadowed = ((VariableShadowFinding)finding).getShadowed(); + if (shadowed.getName().equals(name) && shadowed.lineNumber() == shadowedLine && shadower.lineNumber() == shadowerLine) { + return; + } + } + } + Assertions.fail("No shadow finding found for name '" + name + "' in lines " + shadowerLine + " > " + shadowedLine); + } + + static void assertRedefinition(Set<AbstractFinding> findings, String name, int declLine) { + for (AbstractFinding finding : findings) { + if (finding instanceof MultipleDeclarationFinding) { + Declaration declaration = ((MultipleDeclarationFinding)finding).getDeclaration(); + if (declaration.getName().equals(name) && declaration.lineNumber() == declLine) { + return; + } + } + } + Assertions.fail("No multi-decl finding found for name '" + name + "' in line " + declLine); + } +} diff --git a/scope4m/src/test/java/org/jmodelica/SimpleScopeTest.java b/scope4m/src/test/java/org/jmodelica/SimpleScopeTest.java new file mode 100644 index 0000000..538bd3c --- /dev/null +++ b/scope4m/src/test/java/org/jmodelica/SimpleScopeTest.java @@ -0,0 +1,20 @@ +package org.jmodelica; + +import org.jmodelica.compiler.AbstractFinding; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +public class SimpleScopeTest extends ScopeAnalysisTest { + + @Test + void test() { + + ScopeAnalysis scopeAnalysis = new ScopeAnalysis(); + Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/simple", true, false); + + + } + +} diff --git a/testprograms/modelica/ComplicatedNameLookup.mo b/scope4m/src/test/resources/complicated/complicated.mo similarity index 100% rename from testprograms/modelica/ComplicatedNameLookup.mo rename to scope4m/src/test/resources/complicated/complicated.mo diff --git a/scope4m/src/test/resources/encapsulated/encapsulation.mo b/scope4m/src/test/resources/encapsulated/encapsulation.mo new file mode 100644 index 0000000..d987767 --- /dev/null +++ b/scope4m/src/test/resources/encapsulated/encapsulation.mo @@ -0,0 +1,19 @@ +within ModelicaCompliance.Scoping.NameLookup.Simple; + +model Encapsulation + extends Icons.TestCase; + + encapsulated model A + constant Integer x = abs(-4); + equation + assert(x == 4, "x was not set correctly!"); + end A; + + A a; + + annotation ( + __ModelicaAssociation(TestCase(shouldPass = true, section = {"5.3.1"})), + experiment(StopTime = 0.01), + Documentation( + info = "<html>Tests that builtin functions can be found even if the scope is encapsulated.</html>")); +end Encapsulation; diff --git a/scope4m/src/test/resources/simple/simple.mo b/scope4m/src/test/resources/simple/simple.mo new file mode 100644 index 0000000..75b8e8a --- /dev/null +++ b/scope4m/src/test/resources/simple/simple.mo @@ -0,0 +1,17 @@ +within ModelicaCompliance.Scoping.InnerOuter; + +model SimpleNameLookup + extends Icons.TestCase; + + class A + outer Integer T0; + end A; + + class B + inner Integer T0 = 10; + A a1, a2; // B.T0, B.a1.T0 and B.a2.T0 is the same variable + end B; + + B b; +equation +end SimpleNameLookup; diff --git a/testprograms/modelica/simple/simple.mo b/testprograms/modelica/simple/simple.mo new file mode 100644 index 0000000..75b8e8a --- /dev/null +++ b/testprograms/modelica/simple/simple.mo @@ -0,0 +1,17 @@ +within ModelicaCompliance.Scoping.InnerOuter; + +model SimpleNameLookup + extends Icons.TestCase; + + class A + outer Integer T0; + end A; + + class B + inner Integer T0 = 10; + A a1, a2; // B.T0, B.a1.T0 and B.a2.T0 is the same variable + end B; + + B b; +equation +end SimpleNameLookup; -- GitLab