Skip to content
Snippets Groups Projects
Commit d739aa19 authored by René Schöne's avatar René Schöne
Browse files

0.2.5

- add option to generate separate grammar diagrams
- reverted to separate inheritance type to exclude them if necessary
- added some attributes to silence JastAdd warnings
parent ae54ec88
Branches
No related tags found
No related merge requests found
Pipeline #14509 passed
Showing
with 221 additions and 33 deletions
......@@ -161,7 +161,7 @@ jastadd {
parser.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/parser"
// default options are: '--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false'
extraJastAddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--visitCheck=true"]
extraJastAddOptions = ["--lineColumnNumbers", "--List=JastAddList"]
}
// --- Tests ---
......
aspect Analysis {
syn lazy Folder GrammarFile.defaultFolder() {
Folder result = new Folder();
result.setActive(true);
result.setName(new java.io.File(getFileName()).getName().replace(".relast", "").replace(".ast", ""));
for (Declaration decl : getDeclarationList()) {
if (decl.isTypeDecl() && !decl.asTypeDecl().hasSourceFolder()) {
......@@ -9,4 +10,65 @@ aspect Analysis {
}
return result;
}
syn java.util.Set<TypeDecl> Grammar2Uml.activeTypeDecls() {
java.util.Set<TypeDecl> result = new java.util.HashSet<>();
for (Folder folder : getFolderList()) {
if (folder.getActive()) {
folder.getTypeList().forEach(typeDecl -> result.add(typeDecl));
}
}
return result;
}
syn boolean TypeDecl.relatesToActiveTypeDecl() {
java.util.Set<TypeDecl> activeTypeDecls = grammar2uml().activeTypeDecls();
// self
if (activeTypeDecls.contains(this)) {
return true;
}
// inheritance
if (hasSuperType() && activeTypeDecls.contains(getSuperType())) {
return true;
}
for (TypeDecl subType : getSubTypeList()) {
if (activeTypeDecls.contains(subType)) {
return true;
}
}
// relations
for (Role role : getRoleList()) {
Relation relation = role.containingRelation();
boolean activeRelation = relation.isDirectedRelation() ?
(activeTypeDecls.contains(relation.asDirectedRelation().getSource().getType()) ||
activeTypeDecls.contains(relation.asDirectedRelation().getTarget().getType())) :
(activeTypeDecls.contains(relation.asBidirectionalRelation().getLeft().getType()) ||
activeTypeDecls.contains(relation.asBidirectionalRelation().getRight().getType()));
if (activeRelation) {
return true;
}
}
// containment
for (TypeComponent potentialParent : getPotentialParentList()) {
if (activeTypeDecls.contains(potentialParent.containingTypeDecl())) {
return true;
}
}
for (Component component : getComponentList()) {
if (component.isTypeComponent() && activeTypeDecls.contains(component.asTypeComponent().getTypeDecl())) {
return true;
}
}
return false;
}
inh Relation Role.containingRelation();
eq DirectedRelation.getSource().containingRelation() = this;
eq DirectedRelation.getTarget().containingRelation() = this;
eq BidirectionalRelation.getLeft().containingRelation() = this;
eq BidirectionalRelation.getRight().containingRelation() = this;
}
Grammar2Uml ::= Program <FileName> Folder* <StyleDefinition:StyleDefinition> ;
Folder ::= <Name:String> ;
Folder ::= <Name:String> <Active:boolean> ;
rel Folder.Type* <-> TypeDecl.SourceFolder?;
Style ::= <BackgroundColor> <InlineAsSuperType:boolean> ;
MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* ;
MFolder ::= InnerTypeDecl:MTypeDecl*;
MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* Inheritance:MInheritance* ;
MFolder ::= InnerTypeDecl:MTypeDecl* <Active:boolean> ;
MTypeDecl ::= InnerTokenComponent:MTokenComponent* <Name> Style;
MTokenComponent;
abstract MContainment ::= <Label:String> ;
......@@ -7,6 +7,7 @@ MSingleContainment : MContainment;
MOptContainment : MContainment;
MListContainment : MContainment;
MRelation ::= <Label> <LeftModifier> <RightModifier> <Bidirectional:boolean>;
MInheritance;
rel MGrammar2Uml.Grammar2Uml -> Grammar2Uml;
rel MFolder.Folder -> Folder;
......@@ -16,4 +17,6 @@ rel MContainment.Type -> MTypeDecl;
rel MContainment.Component -> MTypeDecl;
rel MRelation.Left -> MTypeDecl;
rel MRelation.Right -> MTypeDecl;
rel MTypeDecl.SuperClass? -> MTypeDecl;
//rel MTypeDecl.SuperClass? -> MTypeDecl;
rel MInheritance.SuperClass -> MTypeDecl;
rel MInheritance.SubClass -> MTypeDecl;
......@@ -3,16 +3,23 @@ aspect Navigation {
// --- program ---
eq Grammar2Uml.getChild().program() = getProgram();
eq MGrammar2Uml.getChild().program() = getGrammar2Uml().program();
eq Document.getChild().program() = null;
// --- grammar2uml ---
inh Grammar2Uml ASTNode.grammar2uml();
eq Grammar2Uml.getChild().grammar2uml() = this;
eq MGrammar2Uml.getChild().grammar2uml() = getGrammar2Uml();
eq Document.getChild().grammar2uml() = null;
// --- containedFile ---
eq Grammar2Uml.getChild().containedFile() = null;
eq MGrammar2Uml.getChild().containedFile() = null;
eq Document.getChild().containedFile() = null;
// --- containedFileName ---
eq Grammar2Uml.containedFileName() = getFileName();
// --- containingFolder ---
inh MFolder MTypeDecl.containingFolder();
eq MFolder.getInnerTypeDecl().containingFolder() = this;
}
......@@ -27,6 +27,14 @@ aspect AttributesForMustache {
syn boolean MTypeDecl.isAbstract() = getType().getAbstract();
syn boolean MTypeDecl.relatesToActiveTypeDecl() = getType().relatesToActiveTypeDecl();
syn boolean MTypeDecl.folderInactiveAndUnrelated() = !containingFolderActive() && !relatesToActiveTypeDecl();
syn boolean MTypeDecl.folderInactiveAndRelated() = !containingFolderActive() && relatesToActiveTypeDecl();
syn boolean MTypeDecl.containingFolderActive() {
return !getType().hasSourceFolder() || getType().getSourceFolder().getActive();
}
syn String MTypeDecl.backgroundColor() = getStyle().getBackgroundColor();
// --- MTokenComponent ---
......@@ -51,12 +59,24 @@ aspect AttributesForMustache {
// eq MSingleRelation.modifier() = "\"1\"";
// eq MOptRelation.modifier() = "\"0 .. 1\"";
// eq MListRelation.modifier() = "\"*\"";
syn boolean MRelation.bothRelated() = getLeft().relatesToActiveTypeDecl() && getRight().relatesToActiveTypeDecl();
syn boolean MRelation.atLeastOneInActiveFolder() = getLeft().containingFolderActive() ||
getRight().containingFolderActive();
// --- MInheritance ---
syn boolean MInheritance.bothRelated() = getSuperClass().relatesToActiveTypeDecl() &&
getSubClass().relatesToActiveTypeDecl();
syn boolean MInheritance.atLeastOneInActiveFolder() = getSuperClass().containingFolderActive() ||
getSubClass().containingFolderActive();
// --- toMContainment ---
syn MContainment TypeComponent.toMContainment();
eq NormalComponent.toMContainment() = new MSingleContainment();
eq ListComponent.toMContainment() = new MListContainment();
eq OptComponent.toMContainment() = new MOptContainment();
syn boolean MContainment.bothRelated() = getType().relatesToActiveTypeDecl() && getComponent().relatesToActiveTypeDecl();
syn boolean MContainment.atLeastOneInActiveFolder() = getType().containingFolderActive() ||
getComponent().containingFolderActive();
// --- toMRelation ---
syn MRelation Relation.toMRelation();
......@@ -98,6 +118,15 @@ aspect AttributesForMustache {
if (!typeDecl.hasSourceFolder()) {
result.addOtherType(typeDecl.toMustache());
}
if (typeDecl.hasSuperType()) {
Style parentStyle = typeDecl.getSuperType().toMustache().getStyle();
if (!parentStyle.getInlineAsSuperType()) {
MInheritance inheritance = new MInheritance();
inheritance.setSuperClass(typeDecl.getSuperType().toMustache());
inheritance.setSubClass(typeDecl.toMustache());
result.addInheritance(inheritance);
}
}
for (Component component : typeDecl.getComponentList()) {
if (component.isTypeComponent()) {
TypeComponent typeComponent = component.asTypeComponent();
......@@ -137,6 +166,7 @@ aspect AttributesForMustache {
for (TypeDecl typeDecl : getTypeList()) {
result.addInnerTypeDecl(typeDecl.toMustache());
}
result.setActive(this.getActive());
return result;
}
......@@ -157,8 +187,6 @@ aspect AttributesForMustache {
Style parentStyle = this.getSuperType().toMustache().getStyle();
if (parentStyle.getInlineAsSuperType()) {
result.setName(getName() + ":" + this.getSuperType().getName());
} else {
result.setSuperClass(this.getSuperType().toMustache());
}
}
return result;
......
......@@ -12,6 +12,7 @@ import org.jastadd.relast.compiler.CompilerException;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
......@@ -21,6 +22,7 @@ public class Compiler extends AbstractCompiler {
private final Grammar2UmlProcessor processor;
private ValueOption optionOutputFile;
private ValueOption optionInputGrammar2Uml;
private ValueOption optionGenerateSeparateGrammarDiagrams;
private BooleanOption optionDefaultFolders;
private BooleanOption optionHelp;
private BooleanOption optionVersion;
......@@ -69,6 +71,9 @@ public class Compiler extends AbstractCompiler {
optionInputGrammar2Uml = addOption(
new ValueOption("inputGrammar2Uml" , "grammar2uml definition file.")
.needsValue(true));
optionGenerateSeparateGrammarDiagrams = addOption(
new ValueOption("separateGrammarDiagrams" , "Generate separate grammar diagrams according to the given pattern (e.g., 'gen/*.png'). Must contain a '*' for separation.")
.needsValue(true));
optionDefaultFolders = addOption(
new BooleanOption("defaultFolders" ,
"Creates a default folder per grammar file.")
......@@ -113,8 +118,16 @@ public class Compiler extends AbstractCompiler {
}
processor.setUseDefaultFolders(optionDefaultFolders.value())
.setVerbose(optionVerbose.value())
.writeFile(Paths.get(optionOutputFile.value()).toAbsolutePath());
.setVerbose(optionVerbose.value());
processor.writeFile(Paths.get(optionOutputFile.value()).toAbsolutePath());
if (optionGenerateSeparateGrammarDiagrams.isMatched()) {
Path destinationPattern = Path.of(optionGenerateSeparateGrammarDiagrams.value());
Path parent = destinationPattern.getParent();
String extension = Grammar2UmlProcessor.fileExtensionOf(destinationPattern.getFileName());
processor.generateSeparateGrammarDiagrams(parent, extension);
}
return 0;
}
......
......@@ -14,9 +14,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.function.Consumer;
/**
......@@ -29,7 +27,7 @@ public class Grammar2UmlProcessor {
private boolean verbose;
private boolean useDefaultFolders;
private final List<Folder> folders = new ArrayList<>();
private String generatedAspect = null;
private Grammar2Uml grammar2uml = null;
private final List<Consumer<Grammar2Uml>> callbacks = new ArrayList<>();
private StyleDefinition styleDefinition = (typeDecl, style) -> {};
......@@ -66,17 +64,22 @@ public class Grammar2UmlProcessor {
public void writeFile(Path destination) throws CompilerException {
build();
Path parent = destination.toAbsolutePath().getParent();
String generatedAspect = grammar2uml.generateAspect();
ensureDirectory(destination.toAbsolutePath().getParent());
writeFileContent(destination, generatedAspect);
}
private void ensureDirectory(Path directory) throws CompilerException {
try {
Files.createDirectories(parent);
Files.createDirectories(directory);
} catch (IOException e) {
throw new CompilerException("Error creating output dir " + parent, e);
throw new CompilerException("Error creating output dir " + directory, e);
}
if (inputGrammarFiles.isEmpty()) {
throw new CompilerException("No input grammars specified!");
}
private void writeFileContent(Path destination, String generatedAspect) throws CompilerException {
printMessage("Writing output file " + destination);
String extension = fileExtensionOf(destination).toUpperCase();
FileFormatOption plantUmlOption = null;
......@@ -105,13 +108,69 @@ public class Grammar2UmlProcessor {
}
}
public void generateSeparateGrammarDiagrams(Path destinationDirectory, String extension) throws CompilerException {
build();
ensureDirectory(destinationDirectory);
// remember active states, and set them all to false
Set<Folder> previouslyActive = new HashSet<>();
for (Folder folder : grammar2uml.getFolderList()) {
if (folder.getActive()) {
previouslyActive.add(folder);
}
folder.setActive(false);
}
for (Folder folder : grammar2uml.getFolderList()) {
folder.setActive(true);
// generate aspect and write file
grammar2uml.flushTreeCache();
System.out.println("--- " + folder.getName() + " ---");
String generatedAspect = grammar2uml.generateAspect();
writeFileContent(destinationDirectory.resolve(folder.getName() + "." + extension), generatedAspect);
folder.setActive(false);
}
// restore previous active states
for (Folder folder : grammar2uml.getFolderList()) {
if (previouslyActive.contains(folder)) {
folder.setActive(true);
}
}
}
// private void debugAttributes() {
// for (Folder folder : grammar2uml.getFolderList()) {
// System.out.println(folder.getName() + ": active=" + folder.getActive() + ", mFolder.active=" + folder.toMustache().getActive());
// }
// for (TypeDecl typeDecl : grammar2uml.getProgram().typeDecls().stream()
// .sorted(Comparator.comparing(TypeDecl::getName))
// .collect(Collectors.toList())) {
// MTypeDecl mTypeDecl = typeDecl.toMustache();
// System.out.println(typeDecl.getName() + ": " + String.join(", ",
// "relates=" + typeDecl.relatesToActiveTypeDecl(),
// "folderInactive+Unrelated=" + mTypeDecl.folderInactiveAndUnrelated(),
// "folderInactive+Related=" + mTypeDecl.folderInactiveAndRelated(),
// "folderActive=" + mTypeDecl.containingFolderActive()));
// }
// }
private void build() throws CompilerException {
if (generatedAspect != null) {
if (grammar2uml != null) {
return; // already built
}
if (inputGrammarFiles.isEmpty()) {
throw new CompilerException("No input grammars specified!");
}
Program program = new Program();
Grammar2Uml grammar2uml = new Grammar2Uml();
grammar2uml = new Grammar2Uml();
for (String inputGrammarFileName : inputGrammarFiles) {
printMessage("Parsing " + inputGrammarFileName);
GrammarFile inputGrammar;
......@@ -152,8 +211,6 @@ public class Grammar2UmlProcessor {
}
throw new CompilerException("Exiting because of previous errors.");
}
generatedAspect = grammar2uml.generateAspect();
}
private void printMessage(String message) {
......@@ -162,7 +219,7 @@ public class Grammar2UmlProcessor {
}
}
private String fileExtensionOf(Path path) {
static String fileExtensionOf(Path path) {
String fileName = path.toFile().getName();
int lastIndexOfDot = fileName.lastIndexOf(".");
return fileName.substring(lastIndexOfDot + 1);
......
......@@ -28,7 +28,11 @@ public class SimpleMain {
private static void useAPI() throws CompilerException {
Grammar2UmlProcessor processor = new Grammar2UmlProcessor();
// run --args 'src/main/jastadd/Grammar2Uml.relast src/gen/jastadd-sources/relast.preprocessor/RelAst.relast --defaultFolders --output=uml.md'
processor.addGrammar("src/main/jastadd/Grammar2Uml.relast",
processor
.setVerbose(false)
.addFolder()
.addPostProcessing(grammar2Uml -> {})
.addGrammar("src/main/jastadd/Grammar2Uml.relast",
"src/gen/jastadd-sources/relast.preprocessor/RelAst.relast")
.setStyleDefinition((typeDecl, style) -> {
switch (typeDecl.getName()) {
......@@ -41,8 +45,11 @@ public class SimpleMain {
}
})
.setUseDefaultFolders(true);
processor.writeFile(Paths.get("api.md"));
processor.writeFile(Paths.get("api.png"));
System.out.println("== API == ");
// processor.writeFile(Paths.get("api.md"));
// processor.writeFile(Paths.get("api.png"));
System.out.println("== SEPARATE == ");
processor.generateSeparateGrammarDiagrams(Paths.get("."), "png");
}
public static Grammar2Uml createManualAST() {
......
{{#atLeastOneInActiveFolder}}
{{{typeName}}} *-- {{{modifier}}} {{{componentName}}} {{#Label}}: {{Label}}{{/Label}}
{{/atLeastOneInActiveFolder}}
{{#atLeastOneInActiveFolder}}
{{#SuperClass}}"{{{Name}}}"{{/SuperClass}} <|-- {{#SubClass}}"{{{Name}}}"{{/SubClass}}
{{/atLeastOneInActiveFolder}}
{{#atLeastOneInActiveFolder}}
{{{leftName}}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}-[norank]-> {{{rightModifier}}} {{{rightName}}} {{#Label}}: {{Label}}{{/Label}}
{{/atLeastOneInActiveFolder}}
{{#isAbstract}}abstract {{/isAbstract}}class "{{{Name}}}"{{#backgroundColor}} #{{{backgroundColor}}}{{/backgroundColor}}{{#InnerTokenComponents}}{{#first}} {
{{^folderInactiveAndUnrelated}}
{{#isAbstract}}abstract {{/isAbstract}}class "{{{Name}}}"{{#backgroundColor}} #{{{backgroundColor}}}{{/backgroundColor}}{{#folderInactiveAndRelated}} ##[dashed]{{/folderInactiveAndRelated}}{{^folderInactiveAndRelated}}{{#InnerTokenComponents}}{{#first}} {
{{/first}}
{{{type}}} {{{name}}}
{{#last}}
}{{/last}}{{/InnerTokenComponents}}
{{#hasSuperClass}}{{#SuperClass}}"{{{Name}}}"{{/SuperClass}} <|-- "{{{Name}}}"{{/hasSuperClass}}
{{/folderInactiveAndRelated}}
{{/folderInactiveAndUnrelated}}
```plantuml
@startuml
hide circle
hide methods
hide empty members
{{#Folders}}{{> Folder}}{{/Folders}}
{{#OtherTypes}}{{> TypeDecl}}{{/OtherTypes}}
{{#Containments}}{{> Containment}}{{/Containments}}
{{#Relations}}{{> Relation}}{{/Relations}}
{{#Inheritances}}{{> Inheritance}}{{/Inheritances}}
@enduml
```
#Sat May 21 15:41:29 CEST 2022
version=0.2.4
#Fri Aug 26 14:06:56 CEST 2022
version=0.2.5
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment