Skip to content
Snippets Groups Projects
Commit 8d3e2019 authored by Johannes Mey's avatar Johannes Mey
Browse files

initial version of modelica scope tree generation. inheritance not treated properly yet.

parent aa20dd12
Branches
No related tags found
No related merge requests found
Showing
with 243 additions and 14 deletions
/** 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();
}
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;
}
// 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;
......@@ -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[]{})));
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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;
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;
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;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment