Skip to content
Snippets Groups Projects
Commit f7b7733c authored by Reiner Jung's avatar Reiner Jung
Browse files

Updated fxca to use pipe and filter.

parent df1961c8
No related branches found
No related tags found
No related merge requests found
Showing
with 730 additions and 479 deletions
/***************************************************************************
* Copyright (C) 2023 OceanDSL (https://oceandsl.uni-kiel.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package org.oceandsl.analysis.generic.validators;
import java.io.File;
import java.nio.file.Paths;
import com.beust.jcommander.IParameterValidator;
import com.beust.jcommander.ParameterException;
/**
* Check whether the specified path refers to a directory.
*
* @author Reiner Jung
* @since 1.3.0
*/
public class ParentPathIsDirectoryValidator implements IParameterValidator {
@Override
public void validate(final String name, final String value) throws ParameterException {
final File file = Paths.get(value).getParent().toFile();
if (!file.isDirectory()) {
throw new ParameterException(String.format("Parameter %s: path %s is not a directory.", name, value));
}
}
}
/***************************************************************************
* Copyright (C) 2023 OceanDSL (https://oceandsl.uni-kiel.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package org.oceandsl.analysis.generic.validators;
import java.io.File;
import java.nio.file.Paths;
import com.beust.jcommander.IParameterValidator;
import com.beust.jcommander.ParameterException;
/**
* Check whether the path is writable.
*
* @author Reiner Jung
* @since 1.3.0
*
*/
public class ParentPathIsWriteableValidator implements IParameterValidator {
@Override
public void validate(final String name, final String value) throws ParameterException {
final File file = Paths.get(value).getParent().toFile();
if (!file.canWrite()) {
throw new ParameterException(String.format("Parameter %s: parent path %s is not writeable.", name, value));
}
}
}
...@@ -22,29 +22,20 @@ ...@@ -22,29 +22,20 @@
package org.oceandsl.tools.fxca; package org.oceandsl.tools.fxca;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import javax.xml.parsers.ParserConfigurationException;
import com.beust.jcommander.JCommander; import com.beust.jcommander.JCommander;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import kieker.common.configuration.Configuration; import kieker.common.configuration.Configuration;
import kieker.common.exception.ConfigurationException; import kieker.common.exception.ConfigurationException;
import kieker.tools.common.AbstractService; import kieker.tools.common.AbstractService;
import org.oceandsl.tools.fxca.model.FortranModule;
import org.oceandsl.tools.fxca.model.FortranProject;
import org.oceandsl.tools.fxca.model.StatementNode;
import org.oceandsl.tools.fxca.tools.IOUtils; import org.oceandsl.tools.fxca.tools.IOUtils;
/** /**
...@@ -67,127 +58,100 @@ public final class FxcaMain extends AbstractService<TeetimeConfiguration, Settin ...@@ -67,127 +58,100 @@ public final class FxcaMain extends AbstractService<TeetimeConfiguration, Settin
} }
public static void main(final String[] args) { public static void main(final String[] args) {
final Settings settings = new Settings(); final FxcaMain main = new FxcaMain();
JCommander.newBuilder().addObject(settings).build().parse(args);
// final Predicate<Path> notRootPath = path ->
// !path.toAbsolutePath().equals(rootPath.toAbsolutePath());
final Predicate<Path> useDirectory = IOUtils.isDirectory;
final List<Path> directories = new ArrayList<>();
if (settings.isFlat()) {
directories.addAll(settings.getInputDirectoryPaths());
} else {
for (final Path rootPath : settings.getInputDirectoryPaths()) {
try {
directories.addAll(IOUtils.pathsInDirectory(rootPath, useDirectory, useDirectory, true));
} catch (final IOException e) {
FxcaMain.LOGGER.error("Error scanning directory {}: {} ", rootPath, e.getLocalizedMessage());
}
}
}
FxcaMain.LOGGER.info("done scanning directories.");
IOUtils.createDirectory(settings.getOutputDirectoryPath());
final FortranProject projectModel = new FortranProject();
for (final Path directory : directories) {
try {
projectModel.addModulesFromXMLs(directory);
FxcaMain.processModules(projectModel, directory.toAbsolutePath(), settings.getOutputDirectoryPath());
} catch (IOException | ParserConfigurationException | SAXException e) {
FxcaMain.LOGGER.error("Cannot add modules from {}: {}", directory.toAbsolutePath(),
e.getLocalizedMessage());
}
FxcaMain.LOGGER.info("Done");
}
}
private static void processModules(final FortranProject projectModel, final Path inputPath,
final Path outputDirectoryPath) {
final List<FortranModule> namelessModules = new ArrayList<>();
FxcaMain.LOGGER.info("Added modules from {}.", inputPath);
try (final OutputStream outputStream = Files
.newOutputStream(outputDirectoryPath.resolve(FxcaMain.OPERATION_DEFINITIONS))) {
final PrintStream operationListOutput = new PrintStream(outputStream);
operationListOutput.println("file,operation");
for (final FortranModule fortranModule : projectModel) {
if (!fortranModule.isNamedModule()) {
namelessModules.add(fortranModule);
}
FxcaMain.LOGGER.debug("operation declarations:");
try {
for (final String operationName : fortranModule.computeOperationDeclarations()) {
operationListOutput
.println(fortranModule.getXmlFilePath().toAbsolutePath().getFileName().toString() + ","
+ operationName);
}
} catch (ParserConfigurationException | SAXException e) {
FxcaMain.LOGGER.error("Cannot process module {}: {}", fortranModule.getModuleName(),
e.getLocalizedMessage());
}
FxcaMain.LOGGER.debug("subroutine calls of {}: ", fortranModule.getXmlFilePath());
try { try {
fortranModule.subroutineCalls() final int exitCode = main.run("fxtran code analysis", "fxca", args, new Settings());
.forEach(pair -> FxcaMain.LOGGER.info("call: {} --> {}", pair.first, pair.second)); System.exit(exitCode);
} catch (ParserConfigurationException | SAXException e) { } catch (final IllegalArgumentException e) {
FxcaMain.LOGGER.error("Cannot process subroutine calls for module {}: {}", LoggerFactory.getLogger(FxcaMain.class).error("Configuration error: {}", e.getLocalizedMessage());
fortranModule.getModuleName(), e.getLocalizedMessage()); System.exit(1);
} }
}
FxcaMain.LOGGER.debug("function calls of {}:", fortranModule.getXmlFilePath());
try { // private static void processModules(final FortranProject projectModel, final Path inputPath,
fortranModule.functionCalls() // final Path outputDirectoryPath) {
.forEach(pair -> FxcaMain.LOGGER.info("call: {} --> {}", pair.first, pair.second)); // final List<FortranModule> namelessModules = new ArrayList<>();
} catch (ParserConfigurationException | SAXException e) { //
FxcaMain.LOGGER.error("Cannot process function calls for module {}: {}", // FxcaMain.LOGGER.info("Added modules from {}.", inputPath);
fortranModule.getModuleName(), e.getLocalizedMessage()); //
} // try (final OutputStream outputStream = Files
// .newOutputStream(outputDirectoryPath.resolve(FxcaMain.OPERATION_DEFINITIONS))) {
FxcaMain.LOGGER.debug("node types:"); // final PrintStream operationListOutput = new PrintStream(outputStream);
try { //
IOUtils.printWithCommas( // operationListOutput.println("file,operation");
fortranModule.computeAllNodeAttributes(node -> StatementNode.nodeType(node.getNodeType()))); //
} catch (ParserConfigurationException | SAXException e) { // for (final FortranModule fortranModule : projectModel.getModules().values()) {
FxcaMain.LOGGER.error("Cannot output node attribute for module {}: {}", // if (!fortranModule.isNamedModule()) {
fortranModule.getModuleName(), e.getLocalizedMessage()); // namelessModules.add(fortranModule);
} // }
//
FxcaMain.LOGGER.debug("node names:"); // FxcaMain.LOGGER.debug("operation declarations:");
try { //
IOUtils.printWithCommas(fortranModule.computeAllNodeAttributes(node -> node.getNodeName())); // try {
} catch (ParserConfigurationException | SAXException e) { // for (final String operationName : fortranModule.computeOperationDeclarations()) {
FxcaMain.LOGGER.error("Cannot output node names for module {}: {}", fortranModule.getModuleName(), // operationListOutput
e.getLocalizedMessage()); // .println(fortranModule.getXmlFilePath().toAbsolutePath().getFileName().toString() + ","
} // + operationName);
} // }
// } catch (ParserConfigurationException | SAXException e) {
final PrintStream tableOutput = new PrintStream( // FxcaMain.LOGGER.error("Cannot process module {}: {}", fortranModule.getModuleName(),
Files.newOutputStream(outputDirectoryPath.resolve(FxcaMain.CALL_TABLE))); // e.getLocalizedMessage());
final PrintStream errorOutput = new PrintStream( // }
Files.newOutputStream(outputDirectoryPath.resolve(FxcaMain.NOT_FOUND))); //
// FxcaMain.LOGGER.debug("subroutine calls of {}: ", fortranModule.getXmlFilePath());
try { // try {
projectModel.exportCallTable(tableOutput, errorOutput, namelessModules); // fortranModule.subroutineCalls()
} catch (ParserConfigurationException | SAXException e) { // .forEach(pair -> FxcaMain.LOGGER.info("call: {} --> {}", pair.first, pair.second));
FxcaMain.LOGGER.error("Call table export failed: {}", e.getLocalizedMessage()); // } catch (ParserConfigurationException | SAXException e) {
} // FxcaMain.LOGGER.error("Cannot process subroutine calls for module {}: {}",
// fortranModule.getModuleName(), e.getLocalizedMessage());
operationListOutput.close(); // }
tableOutput.close(); //
errorOutput.close(); // FxcaMain.LOGGER.debug("function calls of {}:", fortranModule.getXmlFilePath());
} catch (final IOException e) { // try {
FxcaMain.LOGGER.error("Cannot write {} file: {}", FxcaMain.OPERATION_DEFINITIONS, e.getLocalizedMessage()); // fortranModule.functionCalls()
} // .forEach(pair -> FxcaMain.LOGGER.info("call: {} --> {}", pair.first, pair.second));
} // } catch (ParserConfigurationException | SAXException e) {
// FxcaMain.LOGGER.error("Cannot process function calls for module {}: {}",
// fortranModule.getModuleName(), e.getLocalizedMessage());
// }
//
// FxcaMain.LOGGER.debug("node types:");
// try {
// IOUtils.printWithCommas(
// fortranModule.computeAllNodeAttributes(node -> StatementNode.nodeType(node.getNodeType())));
// } catch (ParserConfigurationException | SAXException e) {
// FxcaMain.LOGGER.error("Cannot output node attribute for module {}: {}",
// fortranModule.getModuleName(), e.getLocalizedMessage());
// }
//
// FxcaMain.LOGGER.debug("node names:");
// try {
// IOUtils.printWithCommas(fortranModule.computeAllNodeAttributes(node -> node.getNodeName()));
// } catch (ParserConfigurationException | SAXException e) {
// FxcaMain.LOGGER.error("Cannot output node names for module {}: {}", fortranModule.getModuleName(),
// e.getLocalizedMessage());
// }
// }
//
// final PrintStream tableOutput = new PrintStream(
// Files.newOutputStream(outputDirectoryPath.resolve(FxcaMain.CALL_TABLE)));
// final PrintStream errorOutput = new PrintStream(
// Files.newOutputStream(outputDirectoryPath.resolve(FxcaMain.NOT_FOUND)));
//
//// try {
//// projectModel.exportCallTable(tableOutput, errorOutput, namelessModules);
//// } catch (ParserConfigurationException | SAXException e) {
//// FxcaMain.LOGGER.error("Call table export failed: {}", e.getLocalizedMessage());
//// }
//
// operationListOutput.close();
// tableOutput.close();
// errorOutput.close();
// } catch (final IOException e) {
// FxcaMain.LOGGER.error("Cannot write {} file: {}", FxcaMain.OPERATION_DEFINITIONS, e.getLocalizedMessage());
// }
// }
@Override @Override
protected TeetimeConfiguration createTeetimeConfiguration() throws ConfigurationException { protected TeetimeConfiguration createTeetimeConfiguration() throws ConfigurationException {
...@@ -206,6 +170,23 @@ public final class FxcaMain extends AbstractService<TeetimeConfiguration, Settin ...@@ -206,6 +170,23 @@ public final class FxcaMain extends AbstractService<TeetimeConfiguration, Settin
@Override @Override
protected boolean checkParameters(final JCommander commander) throws ConfigurationException { protected boolean checkParameters(final JCommander commander) throws ConfigurationException {
IOUtils.createDirectory(this.settings.getOutputDirectoryPath());
final Predicate<Path> useDirectory = IOUtils.isDirectory;
final List<Path> directories = new ArrayList<>();
if (this.settings.isFlat()) {
directories.addAll(this.settings.getInputDirectoryPaths());
} else {
for (final Path rootPath : this.settings.getInputDirectoryPaths()) {
try {
directories.addAll(IOUtils.pathsInDirectory(rootPath, useDirectory, useDirectory, true));
} catch (final IOException e) {
FxcaMain.LOGGER.error("Error scanning directory {}: {} ", rootPath, e.getLocalizedMessage());
}
}
}
return true; return true;
} }
......
...@@ -21,9 +21,10 @@ import java.util.List; ...@@ -21,9 +21,10 @@ import java.util.List;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.converters.PathConverter; import com.beust.jcommander.converters.PathConverter;
import org.oceandsl.analysis.generic.validators.ParentPathIsDirectoryValidator;
import org.oceandsl.analysis.generic.validators.ParentPathIsWriteableValidator;
import org.oceandsl.analysis.generic.validators.PathIsDirectoryValidator; import org.oceandsl.analysis.generic.validators.PathIsDirectoryValidator;
import org.oceandsl.analysis.generic.validators.PathIsReadableValidator; import org.oceandsl.analysis.generic.validators.PathIsReadableValidator;
import org.oceandsl.analysis.generic.validators.PathIsWriteableValidator;
/** /**
* @author Reiner Jung * @author Reiner Jung
...@@ -38,7 +39,7 @@ public class Settings { ...@@ -38,7 +39,7 @@ public class Settings {
@Parameter(names = { "-o", @Parameter(names = { "-o",
"--output" }, required = true, description = "Path where the output files are placed.", converter = PathConverter.class, validateWith = { "--output" }, required = true, description = "Path where the output files are placed.", converter = PathConverter.class, validateWith = {
PathIsWriteableValidator.class, PathIsDirectoryValidator.class }) ParentPathIsWriteableValidator.class, ParentPathIsDirectoryValidator.class })
private Path outputDirectoryPath; private Path outputDirectoryPath;
@Parameter(names = { "-f", @Parameter(names = { "-f",
......
...@@ -20,7 +20,9 @@ import teetime.framework.Configuration; ...@@ -20,7 +20,9 @@ import teetime.framework.Configuration;
import org.oceandsl.analysis.generic.stages.DirectoryProducer; import org.oceandsl.analysis.generic.stages.DirectoryProducer;
import org.oceandsl.analysis.generic.stages.DirectoryScannerStage; import org.oceandsl.analysis.generic.stages.DirectoryScannerStage;
import org.oceandsl.analysis.generic.stages.TableCSVSink; import org.oceandsl.analysis.generic.stages.TableCSVSink;
import org.oceandsl.tools.fxca.stages.ProcessDomStage; import org.oceandsl.tools.fxca.stages.ComputeOutputState;
import org.oceandsl.tools.fxca.stages.ProcessModuleStructureStage;
import org.oceandsl.tools.fxca.stages.ProcessOperationCallStage;
import org.oceandsl.tools.fxca.stages.ReadDomStage; import org.oceandsl.tools.fxca.stages.ReadDomStage;
/** /**
...@@ -36,21 +38,26 @@ public class TeetimeConfiguration extends Configuration { ...@@ -36,21 +38,26 @@ public class TeetimeConfiguration extends Configuration {
public TeetimeConfiguration(final Settings settings) { public TeetimeConfiguration(final Settings settings) {
final DirectoryProducer producer = new DirectoryProducer(settings.getInputDirectoryPaths()); final DirectoryProducer producer = new DirectoryProducer(settings.getInputDirectoryPaths());
final DirectoryScannerStage directoryScannerStage = new DirectoryScannerStage(true, o -> true, final DirectoryScannerStage directoryScannerStage = new DirectoryScannerStage(true, o -> true,
o -> o.endsWith(".xml")); o -> o.getFileName().toString().endsWith(".xml"));
/** computing stages. */ /** computing stages. */
final ReadDomStage readDomStage = new ReadDomStage(); final ReadDomStage readDomStage = new ReadDomStage();
final ProcessDomStage processDomStage = new ProcessDomStage(); final ProcessModuleStructureStage processModuleStructureStage = new ProcessModuleStructureStage();
final ProcessOperationCallStage processOperationCallStage = new ProcessOperationCallStage();
final ComputeOutputState computeOutputStage = new ComputeOutputState();
/** output stages */ /** output stages */
final TableCSVSink operationDefinitionsSink = new TableCSVSink( final TableCSVSink operationDefinitionsSink = new TableCSVSink(
o -> settings.getOutputDirectoryPath().resolve(TeetimeConfiguration.OPERATION_DEFINITIONS), true); o -> settings.getOutputDirectoryPath().resolve(TeetimeConfiguration.OPERATION_DEFINITIONS), true);
final TableCSVSink callTableSink = new TableCSVSink( final TableCSVSink callTableSink = new TableCSVSink(
o -> settings.getOutputDirectoryPath().resolve(TeetimeConfiguration.CALL_TABLE), true); o -> settings.getOutputDirectoryPath().resolve(TeetimeConfiguration.CALL_TABLE), true);
final TableCSVSink notfoundSink = new TableCSVSink( final TableCSVSink notoundSink = new TableCSVSink(
o -> settings.getOutputDirectoryPath().resolve(TeetimeConfiguration.NOT_FOUND), true); o -> settings.getOutputDirectoryPath().resolve(TeetimeConfiguration.NOT_FOUND), true);
this.connectPorts(producer.getOutputPort(), directoryScannerStage.getInputPort()); this.connectPorts(producer.getOutputPort(), directoryScannerStage.getInputPort());
this.connectPorts(directoryScannerStage.getOutputPort(), readDomStage.getInputPort()); this.connectPorts(directoryScannerStage.getOutputPort(), readDomStage.getInputPort());
this.connectPorts(readDomStage.getOutputPort(), processModuleStructureStage.getInputPort());
this.connectPorts(processModuleStructureStage.getOutputPort(), processOperationCallStage.getInputPort());
this.connectPorts(processOperationCallStage.getOutputPort(), computeOutputStage.getInputPort());
} }
} }
...@@ -15,23 +15,15 @@ ...@@ -15,23 +15,15 @@
***************************************************************************/ ***************************************************************************/
package org.oceandsl.tools.fxca.model; package org.oceandsl.tools.fxca.model;
import java.io.IOException; import java.util.ArrayList;
import java.util.Collections; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.oceandsl.tools.fxca.tools.ListTools;
import org.oceandsl.tools.fxca.tools.Pair; import org.oceandsl.tools.fxca.tools.Pair;
import lombok.Getter; import lombok.Getter;
...@@ -45,25 +37,22 @@ public class FortranModule { ...@@ -45,25 +37,22 @@ public class FortranModule {
private static final Logger LOGGER = LoggerFactory.getLogger(FortranModule.class); private static final Logger LOGGER = LoggerFactory.getLogger(FortranModule.class);
@Getter @Getter
private final Set<String> usedModules; private final Set<String> usedModules = new HashSet<>();
@Getter @Getter
private final Set<String> specifiedOperations; private final Set<String> specifiedOperations = new HashSet<>();
@Getter @Getter
private final String moduleName; private final String moduleName;
@Getter @Getter
private final boolean namedModule; private final boolean namedModule;
StatementNode documentElement; @Getter
private final Document document;
public FortranModule(final Document doc) throws ParserConfigurationException, SAXException, IOException { @Getter
this.documentElement = new StatementNode(doc.getDocumentElement()); private final Collection<Pair<Pair<FortranModule, String>, Pair<FortranModule, String>>> calls = new ArrayList<>();
final StatementNode moduleStatement = ListTools.getUniqueElementIfNonEmpty(
this.documentElement.allDescendents(StatementNode.isModuleStatement, true), null);
this.namedModule = (moduleStatement != null);
this.moduleName = this.namedModule ? moduleStatement.getChild(1).getTextContent() : "<no module>";
this.usedModules = this.computeUsedModels();
this.specifiedOperations = this.computeOperationDeclarations();
this.printSummary(); public FortranModule(final String moduleName, final boolean namedModule, final Document document) {
this.moduleName = moduleName;
this.namedModule = namedModule;
this.document = document;
} }
public void printSummary() { public void printSummary() {
...@@ -76,61 +65,4 @@ public class FortranModule { ...@@ -76,61 +65,4 @@ public class FortranModule {
this.specifiedOperations.forEach(name -> FortranModule.LOGGER.debug(" * {}", name)); this.specifiedOperations.forEach(name -> FortranModule.LOGGER.debug(" * {}", name));
} }
public Set<String> computeOperationDeclarations() throws ParserConfigurationException, SAXException, IOException {
return this.documentElement.getDescendentAttributes(StatementNode.isOperationStatement,
operationNode -> StatementNode.getNameOfOperation(operationNode));
}
public List<Pair<String, String>> operationCalls() throws ParserConfigurationException, SAXException, IOException {
return ListTools.ofM(this.subroutineCalls(), this.functionCalls(), Pair.getComparatorFirstSecond());
}
public List<Pair<String, String>> operationCalls(final Predicate<Node> callPredicate,
final Function<Node, String> calledOperation) {
final Set<Pair<String, String>> result = new HashSet<>(); // Check for double entries
final Set<StatementNode> callStatements = this.documentElement.allDescendents(callPredicate, true);
for (final StatementNode callStatement : callStatements) {
final String callee = calledOperation.apply(callStatement);
final String caller = callStatement.getNameOfContainingOperation();
result.add(new Pair<>(caller, callee));
}
return ListTools.ofM(result, Pair.getComparatorFirstSecond());
}
public List<Pair<String, String>> subroutineCalls() throws ParserConfigurationException, SAXException, IOException {
return this.operationCalls(StatementNode.isCallStatement.and(StatementNode.isLocalAccess.negate()),
subroutineCall -> StatementNode.nameOfCalledOperation(subroutineCall));
}
public List<Pair<String, String>> functionCalls() throws ParserConfigurationException, SAXException, IOException {
return this.operationCalls(StatementNode.namedExpressionAccess.and(StatementNode.isLocalAccess.negate()),
functionCall -> StatementNode.nameOfCalledFunction(functionCall));
}
public <T extends Comparable<T>> List<T> computeAllNodeAttributes(final Function<StatementNode, T> extractAttribute)
throws ParserConfigurationException, SAXException, IOException {
final List<T> resultList = ListTools
.ofM(this.documentElement.getDescendentAttributes(node -> true, extractAttribute));
Collections.sort(resultList);
return resultList;
}
public int getNumberOfNodes() throws ParserConfigurationException, SAXException, IOException {
return this.documentElement.getNumberOfDescendants(true);
}
public Set<String> computeUsedModels() {
final Set<String> result = new HashSet<>();
final StatementNode rootNode = this.documentElement;
final Set<StatementNode> useStatements = rootNode.allDescendents(StatementNode.isUseStatement, false);
for (final StatementNode useStatement : useStatements) {
final String usedModuleName = useStatement.getChild(1).getTextContent();
FortranModule.LOGGER.debug("found use statement: {}, module name: {}", useStatement.getTextContent(),
usedModuleName);
result.add(usedModuleName);
}
return result;
}
} }
...@@ -12,7 +12,8 @@ import org.w3c.dom.Node; ...@@ -12,7 +12,8 @@ import org.w3c.dom.Node;
public class LocalExpressionAccess { public class LocalExpressionAccess {
// we often need to search for names that are defined at the current node, which are *not* external // we often need to search for names that are defined at the current node, which are *not*
// external
// calls. Examples are // calls. Examples are
// - common block definitions // - common block definitions
// - local variables // - local variables
...@@ -20,52 +21,38 @@ public class LocalExpressionAccess { ...@@ -20,52 +21,38 @@ public class LocalExpressionAccess {
// //
// This class collects the parameters for looking for these values. // This class collects the parameters for looking for these values.
public record LocalAccessParameters ( public record LocalAccessParameters(Predicate<Node> blockNodeTypeCheckPredicate,
Predicate<Node> blockNodeTypeCheckPredicate, Predicate<Node> outerDelimiterPredicate, Predicate<Node> innerDelimiterPredicate,
Predicate<Node> outerDelimiterPredicate,
Predicate<Node> innerDelimiterPredicate,
Function<Node, String> extractName) { Function<Node, String> extractName) {
}; }
public enum accessType { COMMON_BLOCK, LOCAL_VARIABLE, OPERATION_PARAMETER, OPERATION_CALL }; public enum accessType {
COMMON_BLOCK, LOCAL_VARIABLE, OPERATION_PARAMETER, OPERATION_CALL
static LocalAccessParameters namesInCommonBlocks = }
new LocalAccessParameters(
StatementNode.isCommonStatement, static LocalAccessParameters namesInCommonBlocks = new LocalAccessParameters(StatementNode.isCommonStatement,
StatementNode.isCommonBlockObjectStatement, StatementNode.isCommonBlockObjectStatement, StatementNode.isSmallN,
StatementNode.isSmallN, smallNNode -> StatementNode.getSuccessorNode(smallNNode, "0").getTextContent());
smallNNode -> StatementNode.getSuccessorNode(smallNNode, "0").getTextContent()
); static LocalAccessParameters namesInOperationParameterList = new LocalAccessParameters(
StatementNode.isOperationStatement, StatementNode.isArgN, StatementNode.isSmallN,
static LocalAccessParameters namesInOperationParameterList = smallNNode -> StatementNode.getSuccessorNode(smallNNode, "0").getTextContent());
new LocalAccessParameters(
StatementNode.isOperationStatement, static LocalAccessParameters namesInLocalVariableList = new LocalAccessParameters(StatementNode.isTDeclStmt,
StatementNode.isArgN, StatementNode.isEnDcl, StatementNode.isSmallN,
StatementNode.isSmallN, smallNNode -> StatementNode.getSuccessorNode(smallNNode, "0").getTextContent());
smallNNode -> StatementNode.getSuccessorNode(smallNNode, "0").getTextContent()
); static Set<String> localNamesDefinedInApplyingBlocks(final Node node, final LocalAccessParameters parameters,
final boolean verbose) {
static LocalAccessParameters namesInLocalVariableList =
new LocalAccessParameters( final List<Node> applyingBlocks = new ArrayList<>();
StatementNode.isTDeclStmt,
StatementNode.isEnDcl,
StatementNode.isSmallN,
smallNNode -> StatementNode.getSuccessorNode(smallNNode, "0").getTextContent()
);
static Set<String> localNamesDefinedInApplyingBlocks(Node node, LocalAccessParameters parameters, boolean verbose) {
List<StatementNode> applyingBlocks = new ArrayList<>();
Node current = node; Node current = node;
while (current != null) { while (current != null) {
List<StatementNode> applyingBlocksOnThisLevel = final List<Node> applyingBlocksOnThisLevel = StatementNode.findAll(current,
(new StatementNode(current)).findAll(nnode -> nnode.getPreviousSibling(), nnode -> nnode.getPreviousSibling(), parameters.blockNodeTypeCheckPredicate, true,
parameters.blockNodeTypeCheckPredicate, StatementNode.paranthesisTypes, -1);
true,
StatementNode.paranthesisTypes,
-1);
applyingBlocks.addAll(applyingBlocksOnThisLevel); applyingBlocks.addAll(applyingBlocksOnThisLevel);
current = current.getParentNode(); current = current.getParentNode();
} }
...@@ -73,26 +60,26 @@ public class LocalExpressionAccess { ...@@ -73,26 +60,26 @@ public class LocalExpressionAccess {
return localNamesDefinedInBlocks(applyingBlocks, parameters); // allNamesDefinedInCommonBlocks return localNamesDefinedInBlocks(applyingBlocks, parameters); // allNamesDefinedInCommonBlocks
} }
private static Set<String> localNamesDefinedInBlocks(Collection<StatementNode> applyingBlocks, LocalAccessParameters parameters) { private static Set<String> localNamesDefinedInBlocks(final Collection<Node> applyingBlocks,
Set <String> result = new HashSet<>(); final LocalAccessParameters parameters) {
final Set<String> result = new HashSet<>();
for (StatementNode blockNode : applyingBlocks) { for (final Node blockNode : applyingBlocks) {
result.addAll(localNamesDefinedInBlock(blockNode, parameters)); result.addAll(localNamesDefinedInBlock(blockNode, parameters));
} }
return result; return result;
} }
private static Set<String> localNamesDefinedInBlock(StatementNode blockNode, LocalAccessParameters parameters) private static Set<String> localNamesDefinedInBlock(final Node blockNode, final LocalAccessParameters parameters) {
{
if (!parameters.blockNodeTypeCheckPredicate.test(blockNode)) { if (!parameters.blockNodeTypeCheckPredicate.test(blockNode)) {
throw new IllegalArgumentException("type checking failure."); throw new IllegalArgumentException("type checking failure.");
} }
HashSet<String> result = new HashSet<>(); final HashSet<String> result = new HashSet<>();
for (StatementNode element : blockNode.allDescendents(parameters.outerDelimiterPredicate, true)) { for (final Node element : StatementNode.allDescendents(blockNode, parameters.outerDelimiterPredicate, true)) {
for (StatementNode smallN : element.allDescendents(parameters.innerDelimiterPredicate, true)) { for (final Node smallN : StatementNode.allDescendents(element, parameters.innerDelimiterPredicate, true)) {
result.add(parameters.extractName.apply(smallN)); result.add(parameters.extractName.apply(smallN));
} }
} }
...@@ -100,18 +87,19 @@ public class LocalExpressionAccess { ...@@ -100,18 +87,19 @@ public class LocalExpressionAccess {
return result; return result;
} }
public static boolean isNamedExpressionLocalReference(Node node, LocalAccessParameters parameters) { public static boolean isNamedExpressionLocalReference(final Node node, final LocalAccessParameters parameters) {
if (!StatementNode.namedExpressionAccess.test(node)) { if (!StatementNode.namedExpressionAccess.test(node)) {
return false; return false;
} }
String nameOfCalledFunction = StatementNode.nameOfCalledFunction(node); final String nameOfCalledFunction = StatementNode.nameOfCalledFunction(node);
return LocalExpressionAccess.localNamesDefinedInApplyingBlocks(node, parameters, false).contains(nameOfCalledFunction); return LocalExpressionAccess.localNamesDefinedInApplyingBlocks(node, parameters, false)
.contains(nameOfCalledFunction);
} }
public static accessType typeOfReferenceAccess(Node referenceNode) { public static accessType typeOfReferenceAccess(final Node referenceNode) {
if (isNamedExpressionLocalReference(referenceNode, namesInCommonBlocks)) { if (isNamedExpressionLocalReference(referenceNode, namesInCommonBlocks)) {
return accessType.COMMON_BLOCK; return accessType.COMMON_BLOCK;
...@@ -128,7 +116,7 @@ public class LocalExpressionAccess { ...@@ -128,7 +116,7 @@ public class LocalExpressionAccess {
return accessType.OPERATION_CALL; return accessType.OPERATION_CALL;
} }
public static boolean isLocalAccess(Node referenceNode) { public static boolean isLocalAccess(final Node referenceNode) {
return StatementNode.isCallStatement.or(StatementNode.namedExpressionAccess).test(referenceNode) return StatementNode.isCallStatement.or(StatementNode.namedExpressionAccess).test(referenceNode)
&& typeOfReferenceAccess(referenceNode) != accessType.OPERATION_CALL; && typeOfReferenceAccess(referenceNode) != accessType.OPERATION_CALL;
} }
...@@ -145,11 +133,11 @@ public class LocalExpressionAccess { ...@@ -145,11 +133,11 @@ public class LocalExpressionAccess {
// return StatementNode.nameOfCalledFunction(referenceNode) + suffix; // return StatementNode.nameOfCalledFunction(referenceNode) + suffix;
// } // }
public static String nameOfCalledFunctionOrLocalReference(Node referenceNode) { public static String nameOfCalledFunctionOrLocalReference(final Node referenceNode) {
// rewritten the switch statement because checkstyle cannot parse it // rewritten the switch statement because checkstyle cannot parse it
accessType switchValue = typeOfReferenceAccess(referenceNode); final accessType switchValue = typeOfReferenceAccess(referenceNode);
String suffix = null; String suffix = null;
if (switchValue == accessType.COMMON_BLOCK) { if (switchValue == accessType.COMMON_BLOCK) {
......
...@@ -19,7 +19,6 @@ import java.io.IOException; ...@@ -19,7 +19,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
...@@ -33,12 +32,9 @@ import org.w3c.dom.Node; ...@@ -33,12 +32,9 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.oceandsl.tools.fxca.tools.IndexIterator;
import org.oceandsl.tools.fxca.tools.ListTools; import org.oceandsl.tools.fxca.tools.ListTools;
import org.oceandsl.tools.fxca.tools.Pair; import org.oceandsl.tools.fxca.tools.Pair;
import lombok.experimental.Delegate;
// No own attributes, only accessor functions for nodes. Implemented as class in order to allow // No own attributes, only accessor functions for nodes. Implemented as class in order to allow
// chaining. // chaining.
...@@ -47,10 +43,7 @@ import lombok.experimental.Delegate; ...@@ -47,10 +43,7 @@ import lombok.experimental.Delegate;
* @author Henning Schnoor * @author Henning Schnoor
* @since 1.3.0 * @since 1.3.0
*/ */
public class StatementNode implements Node { public class StatementNode {
@Delegate(types = Node.class)
private final Node node;
public static Predicate<Node> isSubroutineStatement = StatementNode.hasName("subroutine-stmt"); public static Predicate<Node> isSubroutineStatement = StatementNode.hasName("subroutine-stmt");
public static Predicate<Node> isEndSubroutineStatement = StatementNode.hasName("end-subroutine-stmt"); public static Predicate<Node> isEndSubroutineStatement = StatementNode.hasName("end-subroutine-stmt");
...@@ -94,11 +87,6 @@ public class StatementNode implements Node { ...@@ -94,11 +87,6 @@ public class StatementNode implements Node {
public static List<Pair<Predicate<Node>, Predicate<Node>>> paranthesisTypes = List public static List<Pair<Predicate<Node>, Predicate<Node>>> paranthesisTypes = List
.of(StatementNode.endFunctionToBeginFunction, StatementNode.endSubroutineToBeginSubroutine); .of(StatementNode.endFunctionToBeginFunction, StatementNode.endSubroutineToBeginSubroutine);
@Override
public String getNodeName() {
return (this.node == null) ? null : this.node.getNodeName();
}
public static String nameOfCalledFunction(final Node functionCallNode) { public static String nameOfCalledFunction(final Node functionCallNode) {
return StatementNode.getSuccessorNode(functionCallNode, "0,0").getTextContent(); return StatementNode.getSuccessorNode(functionCallNode, "0,0").getTextContent();
} }
...@@ -119,17 +107,6 @@ public class StatementNode implements Node { ...@@ -119,17 +107,6 @@ public class StatementNode implements Node {
return node -> predicate.test(StatementNode.getSuccessorNode(node, path)); return node -> predicate.test(StatementNode.getSuccessorNode(node, path));
} }
public StatementNode(final Node node) {
this.node = node;
if (!StatementNode.satisfiesAssumptions(node)) {
throw new IllegalArgumentException("unexpected node");
}
}
public String getNodeTypeName() {
return StatementNode.nodeType(this.getNodeType());
}
public static String nodeType(final short nodeType) { public static String nodeType(final short nodeType) {
return switch (nodeType) { return switch (nodeType) {
case Node.ELEMENT_NODE -> "Element Node"; case Node.ELEMENT_NODE -> "Element Node";
...@@ -150,24 +127,6 @@ public class StatementNode implements Node { ...@@ -150,24 +127,6 @@ public class StatementNode implements Node {
// Convenience functions // Convenience functions
private Iterable<StatementNode> getChildren() {
return StatementNode.getChildren(this);
}
private static Iterable<StatementNode> getChildren(final Node node) {
return new Iterable<>() {
@Override
public Iterator<StatementNode> iterator() {
return new IndexIterator<>(() -> node.getChildNodes().getLength(),
index -> new StatementNode(node.getChildNodes().item(index)));
}
};
}
public StatementNode getChild(final int index) {
return new StatementNode(this.getChildNodes().item(index));
}
public static int printNode(final Node node, final int depth) { public static int printNode(final Node node, final int depth) {
String spaces = ""; String spaces = "";
for (int i = 0; i < depth; i++) { for (int i = 0; i < depth; i++) {
...@@ -180,7 +139,8 @@ public class StatementNode implements Node { ...@@ -180,7 +139,8 @@ public class StatementNode implements Node {
System.out.println(spaces + "[node text content] " + node.getTextContent()); System.out.println(spaces + "[node text content] " + node.getTextContent());
System.out.println(spaces + "[node #kids] " + node.getChildNodes().getLength()); System.out.println(spaces + "[node #kids] " + node.getChildNodes().getLength());
for (final StatementNode child : StatementNode.getChildren(node)) { for (int i = 0; i < node.getChildNodes().getLength(); i++) {
final Node child = node.getChildNodes().item(i);
numberOfPrintedNodes += StatementNode.printNode(child, depth + 1); numberOfPrintedNodes += StatementNode.printNode(child, depth + 1);
} }
return numberOfPrintedNodes; return numberOfPrintedNodes;
...@@ -211,12 +171,8 @@ public class StatementNode implements Node { ...@@ -211,12 +171,8 @@ public class StatementNode implements Node {
* predicates) { return node -> testNodeAndFirstChildPredicateChain(node, predicates); } * predicates) { return node -> testNodeAndFirstChildPredicateChain(node, predicates); }
*/ */
public static StatementNode getSuccessorNode(final Node node, final String path) { // TODO this is not doing the right thing
public static Node getSuccessorNode(final Node node, final String path) {
if (path.isEmpty()) {
return new StatementNode(node);
}
final String firstNumber = StringUtils.substringBefore(path, ","); final String firstNumber = StringUtils.substringBefore(path, ",");
final String nextPath = StringUtils.substringAfter(path, ","); final String nextPath = StringUtils.substringAfter(path, ",");
final int childIndex = Integer.parseInt(firstNumber); final int childIndex = Integer.parseInt(firstNumber);
...@@ -224,8 +180,12 @@ public class StatementNode implements Node { ...@@ -224,8 +180,12 @@ public class StatementNode implements Node {
if (children.getLength() < childIndex) { if (children.getLength() < childIndex) {
return null; return null;
} }
if (nextPath.isEmpty()) {
return node.getChildNodes().item(childIndex);
} else {
return StatementNode.getSuccessorNode(node.getChildNodes().item(childIndex), nextPath); return StatementNode.getSuccessorNode(node.getChildNodes().item(childIndex), nextPath);
} }
}
/* /*
* private static ASTNode getFirstChildChain(Node node, int depth) { * private static ASTNode getFirstChildChain(Node node, int depth) {
...@@ -238,9 +198,9 @@ public class StatementNode implements Node { ...@@ -238,9 +198,9 @@ public class StatementNode implements Node {
*/ */
// NOTE: Only terminates if nextNode eventually returns null or a matching element. // NOTE: Only terminates if nextNode eventually returns null or a matching element.
private StatementNode findFirst(final Function<Node, Node> nextNode, final Predicate<Node> condition, private static Node findFirst(final Node parent, final Function<Node, Node> nextNode,
final boolean includeSelf) { final Predicate<Node> condition, final boolean includeSelf) {
return this.findFirst(nextNode, condition, includeSelf, null); return findFirst(parent, nextNode, condition, includeSelf, null);
} }
// paranthesistypes: contains pairs of "opening paranthesis" and "closed paranthesis" // paranthesistypes: contains pairs of "opening paranthesis" and "closed paranthesis"
...@@ -255,32 +215,33 @@ public class StatementNode implements Node { ...@@ -255,32 +215,33 @@ public class StatementNode implements Node {
// "end function declaration" elements appear. // "end function declaration" elements appear.
// NOTE: Only terminates if nextNode eventually returns null or a matching element. // NOTE: Only terminates if nextNode eventually returns null or a matching element.
private StatementNode findFirst(final Function<Node, Node> nextNode, final Predicate<Node> condition, private static Node findFirst(final Node parent, final Function<Node, Node> nextNode,
final boolean includeSelf, final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesesTypes) { final Predicate<Node> condition, final boolean includeSelf,
final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesesTypes) {
final List<StatementNode> result = this.findAll(nextNode, condition, includeSelf, paranthesesTypes, 1); final List<Node> result = findAll(parent, nextNode, condition, includeSelf, paranthesesTypes, 1);
return result.isEmpty() ? null : result.get(0); return result.isEmpty() ? null : result.get(0);
} }
List<StatementNode> findAll(final Function<Node, Node> nextNode, final Predicate<Node> condition, public static List<Node> findAll(final Node parent, final Function<Node, Node> nextNode,
final boolean includeSelf, final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesesTypes, final Predicate<Node> condition, final boolean includeSelf,
final int maxElementsToFind) { final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesesTypes, final int maxElementsToFind) {
final List<StatementNode> result = new ArrayList<>(); final List<Node> result = new ArrayList<>();
final int numberparanthesesTypes = (paranthesesTypes == null) ? 0 : paranthesesTypes.size(); final int numberparanthesesTypes = paranthesesTypes == null ? 0 : paranthesesTypes.size();
final int[] openParanthesis = new int[numberparanthesesTypes]; final int[] openParanthesis = new int[numberparanthesesTypes];
Node current = this; Node current = parent;
boolean inParanthesisInterval = false; boolean inParanthesisInterval = false;
// End if we do not have anywhere to search, or we have reached the limit (where "-1" counts // End if we do not have anywhere to search, or we have reached the limit (where "-1" counts
// as "no limit"). // as "no limit").
while ((current != null) && ((result.size() < maxElementsToFind) || (maxElementsToFind == -1))) { while (current != null && (result.size() < maxElementsToFind || maxElementsToFind == -1)) {
if (!inParanthesisInterval && condition.test(current) && ((current != this) || includeSelf)) { if (!inParanthesisInterval && condition.test(current) && (current != parent || includeSelf)) {
result.add(new StatementNode(current)); result.add(current);
} }
inParanthesisInterval = false; inParanthesisInterval = false;
...@@ -303,49 +264,52 @@ public class StatementNode implements Node { ...@@ -303,49 +264,52 @@ public class StatementNode implements Node {
// Check for nextNode-stationaly points // Check for nextNode-stationaly points
final Node nextNodeResult = nextNode.apply(current); final Node nextNodeResult = nextNode.apply(current);
current = (current == nextNodeResult) ? null : nextNodeResult; current = current == nextNodeResult ? null : nextNodeResult;
} }
return result; return result;
} }
private boolean hasConnectedWith(final Function<Node, Node> nextNode, final Predicate<Node> condition, private static boolean hasConnectedWith(final Node parent, final Function<Node, Node> nextNode,
final boolean includeSelf) { final Predicate<Node> condition, final boolean includeSelf) {
return this.findFirst(nextNode, condition, includeSelf) != null; return findFirst(parent, nextNode, condition, includeSelf) != null;
} }
private boolean hasLeftSibling(final Predicate<Node> condition, final boolean includeSelf) { private static boolean hasLeftSibling(final Node parent, final Predicate<Node> condition,
return this.hasConnectedWith(node -> node.getPreviousSibling(), condition, includeSelf); final boolean includeSelf) {
return hasConnectedWith(parent, node -> node.getPreviousSibling(), condition, includeSelf);
} }
private StatementNode firstLeftSibling(final Predicate<Node> condition, final boolean includeSelf, private static Node firstLeftSibling(final Node parent, final Predicate<Node> condition, final boolean includeSelf,
final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesisTypes) { final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesisTypes) {
return this.findFirst(node -> node.getPreviousSibling(), condition, includeSelf, paranthesisTypes); return findFirst(parent, node -> node.getPreviousSibling(), condition, includeSelf, paranthesisTypes);
} }
private StatementNode firstAncestor(final Predicate<Node> condition, final boolean includeSelf) { private static Node firstAncestor(final Node parent, final Predicate<Node> condition, final boolean includeSelf) {
return this.findFirst(node -> node.getParentNode(), condition, includeSelf); return findFirst(parent, node -> node.getParentNode(), condition, includeSelf);
} }
public Set<StatementNode> allDescendents(final Predicate<Node> condition, final boolean includeSelf) { public static Set<Node> allDescendents(final Node node, final Predicate<Node> condition,
return this.addAllDescendentsTo(condition, includeSelf, new HashSet<>()); final boolean includeSelf) {
return addAllDescendentsTo(node, condition, includeSelf, new HashSet<>());
} }
private <T extends Collection<StatementNode>> T addAllDescendentsTo(final Predicate<Node> condition, private static <T extends Collection<Node>> T addAllDescendentsTo(final Node node, final Predicate<Node> condition,
final boolean includeSelf, final T addToThese) { final boolean includeSelf, final T addToThese) {
if (condition.test(this) && includeSelf) { if (condition.test(node) && includeSelf) {
addToThese.add(this); addToThese.add(node);
} }
for (final StatementNode child : this.getChildren()) { for (int i = 0; i < node.getChildNodes().getLength(); i++) {
child.addAllDescendentsTo(condition, true, addToThese); final Node child = node.getChildNodes().item(i);
addAllDescendentsTo(child, condition, true, addToThese);
} }
return addToThese; return addToThese;
} }
public static String getNameOfOperation(final StatementNode operationStatement) { public static String getNameOfOperation(final Node operationStatement) {
if (StatementNode.isSubroutineStatement.test(operationStatement)) { if (StatementNode.isSubroutineStatement.test(operationStatement)) {
return StatementNode.getNameOfOperation(operationStatement, StatementNode.isSubroutineName); return StatementNode.getNameOfOperation(operationStatement, StatementNode.isSubroutineName);
...@@ -356,36 +320,65 @@ public class StatementNode implements Node { ...@@ -356,36 +320,65 @@ public class StatementNode implements Node {
throw new IllegalArgumentException("Node is neither a function nor a subroutine statement."); throw new IllegalArgumentException("Node is neither a function nor a subroutine statement.");
} }
public static String getNameOfOperation(final StatementNode operationStatement, public static String getNameOfOperation(final Node operationStatement, final Predicate<Node> namePredicate) {
final Predicate<Node> namePredicate) { final Set<Node> nameNodes = allDescendents(operationStatement, namePredicate, true);
final Set<StatementNode> nameNodes = operationStatement.allDescendents(namePredicate, true);
return ListTools.getUniqueElement(nameNodes).getTextContent(); return ListTools.getUniqueElement(nameNodes).getTextContent();
} }
public StatementNode findContainingStatement(final Predicate<Node> condition) { public static <T> Set<T> getDescendentAttributes(final Node node, final Predicate<Node> predicate,
return this.findContainingStatement(condition, null); final Function<Node, T> extractAttribute) throws ParserConfigurationException, SAXException, IOException {
return allDescendents(node, predicate, true).stream().map(extractAttribute).collect(Collectors.toSet());
}
public static List<Pair<String, String>> operationCalls(final Node node, final Predicate<Node> callPredicate,
final Function<Node, String> calledOperation) {
final Set<Pair<String, String>> result = new HashSet<>(); // Check for double entries
final Set<Node> callStatements = allDescendents(node, callPredicate, true);
for (final Node callStatement : callStatements) {
final String callee = calledOperation.apply(callStatement);
final String caller = getNameOfContainingOperation(callStatement);
result.add(new Pair<>(caller, callee));
}
return ListTools.ofM(result, Pair.getComparatorFirstSecond());
}
public static List<Pair<String, String>> subroutineCalls(final Node node)
throws ParserConfigurationException, SAXException, IOException {
return operationCalls(node, StatementNode.isCallStatement.and(StatementNode.isLocalAccess.negate()),
subroutineCall -> StatementNode.nameOfCalledOperation(subroutineCall));
} }
public StatementNode findContainingStatement(final Predicate<Node> condition, public static List<Pair<String, String>> functionCalls(final Node node)
throws ParserConfigurationException, SAXException, IOException {
return operationCalls(node, StatementNode.namedExpressionAccess.and(StatementNode.isLocalAccess.negate()),
functionCall -> StatementNode.nameOfCalledFunction(functionCall));
}
/** ---------------------------------- */
public static Node findContainingStatement(final Node parent, final Predicate<Node> condition) {
return findContainingStatement(parent, condition, null);
}
public static Node findContainingStatement(final Node parent, final Predicate<Node> condition,
final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesisTypes) { final List<Pair<Predicate<Node>, Predicate<Node>>> paranthesisTypes) {
final Predicate<Node> hasSuchANodeAsLeftSibling = node -> new StatementNode(node).hasLeftSibling(condition, final Predicate<Node> hasSuchANodeAsLeftSibling = node -> hasLeftSibling(node, condition, false);
false); final Node siblingOfSuchNode = firstAncestor(parent, hasSuchANodeAsLeftSibling, !condition.test(parent));
final StatementNode siblingOfSuchNode = this.firstAncestor(hasSuchANodeAsLeftSibling, !condition.test(this));
if (siblingOfSuchNode == null) { if (siblingOfSuchNode == null) {
return null; return null;
} }
final StatementNode suchStatement = siblingOfSuchNode.firstLeftSibling(condition, true, paranthesisTypes); return firstLeftSibling(siblingOfSuchNode, condition, true, paranthesisTypes);
return suchStatement;
} }
public String getNameOfContainingOperation() { private static String getNameOfContainingOperation(final Node node) {
final StatementNode containingOperationStatement = this final Node containingOperationStatement = findContainingStatement(node, StatementNode.isOperationStatement,
.findContainingStatement(StatementNode.isOperationStatement, StatementNode.paranthesisTypes); StatementNode.paranthesisTypes);
return (containingOperationStatement == null) ? "<root>" return containingOperationStatement == null ? "<root>"
: StatementNode.getNameOfOperation(containingOperationStatement); : StatementNode.getNameOfOperation(containingOperationStatement);
} }
...@@ -402,11 +395,11 @@ public class StatementNode implements Node { ...@@ -402,11 +395,11 @@ public class StatementNode implements Node {
final short type = node.getNodeType(); final short type = node.getNodeType();
if ((type == Node.TEXT_NODE) && (node.getChildNodes().getLength() > 0)) { if (type == Node.TEXT_NODE && node.getChildNodes().getLength() > 0) {
throw new IllegalArgumentException("text node with children"); throw new IllegalArgumentException("text node with children");
} }
if (("call-stmt").equals(node.getNodeName()) && (node.getChildNodes().getLength() < 2)) { if ("call-stmt".equals(node.getNodeName()) && node.getChildNodes().getLength() < 2) {
StatementNode.printNode(node, 0); StatementNode.printNode(node, 0);
throw new IllegalArgumentException("call statement with < 2 children"); throw new IllegalArgumentException("call statement with < 2 children");
} }
...@@ -433,17 +426,8 @@ public class StatementNode implements Node { ...@@ -433,17 +426,8 @@ public class StatementNode implements Node {
return true; return true;
} }
public boolean satisfiesAssumptions() { public int getNumberOfDescendants(final Node parent, final boolean countSelf) {
return StatementNode.satisfiesAssumptions(this); return allDescendents(parent, node -> true, countSelf).size();
}
public int getNumberOfDescendants(final boolean countSelf) {
return this.allDescendents(node -> true, countSelf).size();
} }
public <T> Set<T> getDescendentAttributes(final Predicate<Node> predicate,
final Function<StatementNode, T> extractAttribute)
throws ParserConfigurationException, SAXException, IOException {
return this.allDescendents(predicate, true).stream().map(extractAttribute).collect(Collectors.toSet());
}
} }
...@@ -48,15 +48,15 @@ public class CallTableStage extends AbstractTransformation<FortranProject, Table ...@@ -48,15 +48,15 @@ public class CallTableStage extends AbstractTransformation<FortranProject, Table
final List<FortranModule> globalModules) throws ParserConfigurationException, SAXException, IOException { final List<FortranModule> globalModules) throws ParserConfigurationException, SAXException, IOException {
tableOut.println("callerfilename,callermodule,callerfunction,calleefilename,calleemodule,calleefunction"); tableOut.println("callerfilename,callermodule,callerfunction,calleefilename,calleemodule,calleefunction");
this.logger.debug("Calls to operations that could not be found:"); this.logger.debug("Calls to operations that could not be found:");
for (final FortranModule module : this) { for (final FortranModule module : globalModules) {
for (final Pair<String, String> call : module.operationCalls()) { for (final Pair<String, String> call : module.operationCalls()) {
final String callerFunctionName = call.first; final String callerFunctionName = call.first;
final String callerFileName = module.getXmlFilePath().toAbsolutePath().getFileName().toString(); final String callerFileName = module.getXmlFilePath().toAbsolutePath().getFileName().toString();
final String calleeFunctionName = call.second; final String calleeFunctionName = call.second;
final FortranModule calleeXML = this.resolveCallee(module, calleeFunctionName, globalModules); final FortranModule calleeXML = this.resolveCallee(module, calleeFunctionName, globalModules);
final String calleeFileName = (calleeXML == null) ? "<unknown>" final String calleeFileName = calleeXML == null ? "<unknown>"
: calleeXML.getXmlFilePath().toAbsolutePath().getFileName().toString(); : calleeXML.getXmlFilePath().toAbsolutePath().getFileName().toString();
final String calleeModuleName = (calleeXML == null) ? "<unknown>" : calleeXML.getModuleName(); final String calleeModuleName = calleeXML == null ? "<unknown>" : calleeXML.getModuleName();
tableOut.println(callerFileName + ", " + module.getModuleName() + ", " + callerFunctionName + ", " tableOut.println(callerFileName + ", " + module.getModuleName() + ", " + callerFunctionName + ", "
+ calleeFileName + ", " + calleeModuleName + ", " + calleeFunctionName); + calleeFileName + ", " + calleeModuleName + ", " + calleeFunctionName);
if (calleeXML == null) { if (calleeXML == null) {
...@@ -85,12 +85,12 @@ public class CallTableStage extends AbstractTransformation<FortranProject, Table ...@@ -85,12 +85,12 @@ public class CallTableStage extends AbstractTransformation<FortranProject, Table
// ones. // ones.
for (final String usedModuleName : callerModule.getUsedModules()) { for (final String usedModuleName : callerModule.getUsedModules()) {
final FortranModule moduleXML = this.moduleNames.get(usedModuleName); // final FortranModule moduleXML = moduleNames.get(usedModuleName);
if (moduleXML == null) { // if (moduleXML == null) {
this.logger.warn("MODULE NOT FOUND: [{}]", usedModuleName); // this.logger.warn("MODULE NOT FOUND: [{}]", usedModuleName);
} else { // } else {
usedModules.add(moduleXML); // usedModules.add(moduleXML);
} // }
} }
if (globalModules != null) { if (globalModules != null) {
......
package org.oceandsl.tools.fxca.stages;
import teetime.framework.AbstractConsumerStage;
import teetime.framework.OutputPort;
import org.oceandsl.analysis.code.stages.data.StringValueHandler;
import org.oceandsl.analysis.code.stages.data.Table;
import org.oceandsl.analysis.code.stages.data.ValueConversionErrorException;
import org.oceandsl.tools.fxca.model.FortranModule;
import org.oceandsl.tools.fxca.model.FortranProject;
import org.oceandsl.tools.fxca.tools.Pair;
public class ComputeOutputState extends AbstractConsumerStage<FortranProject> {
private static final String SOURCE_PATH = "callerfilename";
private static final String SOURCE_MODULE = "callermodule";
private static final String SOURCE_OPERATION = "callerfunction";
private static final String TARGET_PATH = "calleefilename";
private static final String TARGET_MODULE = "calleemodule";
private static final String TARGET_OPERATION = "calleefunction";
private final OutputPort<Table> callsOutputPort = this.createOutputPort(Table.class);
private final OutputPort<Table> callTableOutputPort = this.createOutputPort(Table.class);
private final OutputPort<Table> notFoundOutputPort = this.createOutputPort(Table.class);
@Override
protected void execute(final FortranProject project) throws Exception {
final Table callsTable = new Table("calls", new StringValueHandler(SOURCE_PATH),
new StringValueHandler(SOURCE_MODULE), new StringValueHandler(SOURCE_OPERATION),
new StringValueHandler(TARGET_PATH), new StringValueHandler(TARGET_MODULE),
new StringValueHandler(TARGET_OPERATION));
project.getModules().values().forEach(module -> {
module.getCalls().forEach(call -> {
final Pair<FortranModule, String> caller = call.getFirst();
final Pair<FortranModule, String> callee = call.getSecond();
if (caller != null) {
final FortranModule callerModule = caller.getFirst();
final String callerPath;
final String callerModuleName;
final String callerOperation = caller.getSecond();
if (callerModule.isNamedModule()) {
callerPath = callerModule.getDocument().getBaseURI();
callerModuleName = callerModule.getModuleName();
} else {
callerPath = callerModule.getModuleName();
callerModuleName = "<no-module>";
}
if (callee != null) {
final FortranModule calleeModule = callee.getFirst();
final String calleePath;
final String calleeModuleName;
final String calleeOperation = callee.getSecond();
if (calleeModule.isNamedModule()) {
calleePath = calleeModule.getDocument().getBaseURI();
calleeModuleName = calleeModule.getModuleName();
} else {
calleePath = calleeModule.getModuleName();
calleeModuleName = "<no-module>";
}
try {
callsTable.addRow(callerPath, callerModuleName, callerOperation, calleePath,
calleeModuleName, calleeOperation);
} catch (final ValueConversionErrorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
this.logger.debug("Caller {} {} {} has no callee ", callerPath, callerModuleName,
callerOperation);
}
} else {
if (callee != null) {
final FortranModule calleeModule = callee.getFirst();
final String calleePath;
final String calleeModuleName;
final String calleeOperation = callee.getSecond();
if (calleeModule.isNamedModule()) {
calleePath = calleeModule.getDocument().getBaseURI();
calleeModuleName = calleeModule.getModuleName();
} else {
calleePath = calleeModule.getModuleName();
calleeModuleName = "<no-module>";
}
this.logger.debug("No caller for callee {} {} {}", calleePath, calleeModuleName,
calleeOperation);
} else {
this.logger.debug("Empty call");
}
}
});
});
this.callsOutputPort.send(callsTable);
}
}
...@@ -16,16 +16,21 @@ ...@@ -16,16 +16,21 @@
package org.oceandsl.tools.fxca.stages; package org.oceandsl.tools.fxca.stages;
import java.io.IOException; import java.io.IOException;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import teetime.stage.basic.AbstractTransformation; import teetime.stage.basic.AbstractTransformation;
import org.oceandsl.tools.fxca.model.FortranModule; import org.oceandsl.tools.fxca.model.FortranModule;
import org.oceandsl.tools.fxca.model.FortranProject; import org.oceandsl.tools.fxca.model.FortranProject;
import org.oceandsl.tools.fxca.model.StatementNode;
import org.oceandsl.tools.fxca.tools.ListTools;
/** /**
* *
...@@ -33,17 +38,30 @@ import org.oceandsl.tools.fxca.model.FortranProject; ...@@ -33,17 +38,30 @@ import org.oceandsl.tools.fxca.model.FortranProject;
* @author Reiner Jung * @author Reiner Jung
* *
*/ */
public class ProcessDomStage extends AbstractTransformation<Document, FortranProject> { public class ProcessModuleStructureStage extends AbstractTransformation<Document, FortranProject> {
private final FortranProject project; private final FortranProject project;
public ProcessDomStage() { public ProcessModuleStructureStage() {
this.project = new FortranProject(); this.project = new FortranProject();
} }
@Override @Override
protected void execute(final Document document) throws Exception { protected void execute(final Document document) throws Exception {
this.addModule(document); final Element documentElement = document.getDocumentElement();
final Node moduleStatement = ListTools.getUniqueElementIfNonEmpty(
StatementNode.allDescendents(documentElement, StatementNode.isModuleStatement, true), null);
final boolean namedModule = moduleStatement != null;
final String moduleName = namedModule ? moduleStatement.getChildNodes().item(1).getTextContent()
: document.getBaseURI();
final FortranModule module = new FortranModule(moduleName, namedModule, document);
this.computeUsedModels(module, documentElement);
this.computeOperationDeclarations(module, documentElement);
this.project.getModules().put(module.getModuleName(), module);
} }
@Override @Override
...@@ -52,8 +70,22 @@ public class ProcessDomStage extends AbstractTransformation<Document, FortranPro ...@@ -52,8 +70,22 @@ public class ProcessDomStage extends AbstractTransformation<Document, FortranPro
super.onTerminating(); super.onTerminating();
} }
public void addModule(final Document document) throws ParserConfigurationException, SAXException, IOException { private void computeUsedModels(final FortranModule module, final Element rootNode) {
final FortranModule module = new FortranModule(document); final Set<Node> useStatements = StatementNode.allDescendents(rootNode, StatementNode.isUseStatement, false);
this.project.getModules().put(module.getModuleName(), module); for (final Node useStatement : useStatements) {
final String usedModuleName = useStatement.getChildNodes().item(1).getTextContent();
this.logger.debug("found use statement: {}, module name: {}", useStatement.getTextContent(),
usedModuleName);
module.getUsedModules().add(usedModuleName);
}
} }
public void computeOperationDeclarations(final FortranModule module, final Element documentElement)
throws ParserConfigurationException, SAXException, IOException {
StatementNode
.getDescendentAttributes(documentElement, StatementNode.isOperationStatement,
operationNode -> StatementNode.getNameOfOperation(operationNode))
.forEach(operation -> module.getSpecifiedOperations().add(operation));
}
} }
/***************************************************************************
* Copyright (C) 2023 OceanDSL (https://oceandsl.uni-kiel.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package org.oceandsl.tools.fxca.stages;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import teetime.stage.basic.AbstractFilter;
import org.oceandsl.tools.fxca.model.FortranModule;
import org.oceandsl.tools.fxca.model.FortranProject;
import org.oceandsl.tools.fxca.model.StatementNode;
import org.oceandsl.tools.fxca.tools.Pair;
/**
* @author Henning Schnoor -- initial contribution
* @author Reiner Jung
*
*/
public class ProcessOperationCallStage extends AbstractFilter<FortranProject> {
@Override
protected void execute(final FortranProject project) throws Exception {
project.getModules().values().forEach(module -> {
final Element element = module.getDocument().getDocumentElement();
try {
final List<Pair<String, String>> calls = StatementNode.subroutineCalls(element);
calls.forEach(call -> {
final Pair<FortranModule, String> caller = this.findOperation(project.getModules().values(),
call.getFirst());
final Pair<FortranModule, String> callee = this.findOperation(project.getModules().values(),
call.getSecond());
if (caller == null) {
this.logger.debug("Caller not found for {}", call.getFirst());
}
if (callee == null) {
this.logger.debug("Callee not found for {}", call.getSecond());
}
module.getCalls().add(new Pair<>(caller, callee));
});
} catch (ParserConfigurationException | SAXException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
this.outputPort.send(project);
}
private Pair<FortranModule, String> findOperation(final Collection<FortranModule> modules, final String signature) {
final Optional<FortranModule> moduleOptional = modules.stream()
.filter(module -> module.getSpecifiedOperations().contains(signature)).findFirst();
if (moduleOptional.isPresent()) {
return new Pair<>(moduleOptional.get(), signature);
} else {
return null;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment