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

API + some enhancements

- added programmable interface (Grammar2UmlProcessor)
- add types to tokens
- API: add styling for TypeDecl
- API: add option to inline superclass
parent 93761f3d
Branches
No related tags found
No related merge requests found
Showing
with 323 additions and 210 deletions
...@@ -174,6 +174,13 @@ ext { ...@@ -174,6 +174,13 @@ ext {
application.mainClassName = "${mainClassName}" application.mainClassName = "${mainClassName}"
task simpleRun(type: JavaExec) {
group 'application'
classpath sourceSets.main.runtimeClasspath
main = "de.tudresden.inf.st.jastadd.grammar2uml.compiler.SimpleMain"
}
jar { jar {
manifest.attributes "Main-Class": "${mainClassName}" manifest.attributes "Main-Class": "${mainClassName}"
} }
......
aspect GrammarTypes {
public interface StyleDefinition extends java.util.function.BiConsumer<TypeDecl, Style> {
}
}
Grammar2Uml ::= Program <FileName> Folder* ; Grammar2Uml ::= Program <FileName> Folder* <StyleDefinition:StyleDefinition> ;
Folder ::= <Name:String> ; Folder ::= <Name:String> ;
rel Folder.Type* <-> TypeDecl.SourceFolder?; rel Folder.Type* <-> TypeDecl.SourceFolder?;
Style ::= <BackgroundColor> <InlineAsSuperType:boolean> ;
MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* Inheritance:MInheritance* ; MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* ;
MFolder ::= InnerTypeDecl:MTypeDecl*; MFolder ::= InnerTypeDecl:MTypeDecl*;
MTypeDecl ::= InnerTokenComponent:MTokenComponent*; MTypeDecl ::= InnerTokenComponent:MTokenComponent* <Name> Style;
MTokenComponent; MTokenComponent;
abstract MContainment ::= <Label:String> ; abstract MContainment ::= <Label:String> ;
MSingleContainment : MContainment; MSingleContainment : MContainment;
MOptContainment : MContainment; MOptContainment : MContainment;
MListContainment : MContainment; MListContainment : MContainment;
MRelation ::= <Label> <LeftModifier> <RightModifier> <Bidirectional:boolean>; MRelation ::= <Label> <LeftModifier> <RightModifier> <Bidirectional:boolean>;
MInheritance ;
rel MGrammar2Uml.Grammar2Uml -> Grammar2Uml; rel MGrammar2Uml.Grammar2Uml -> Grammar2Uml;
rel MFolder.Folder -> Folder; rel MFolder.Folder -> Folder;
rel MTypeDecl.Type -> TypeDecl; rel MTypeDecl.Type -> TypeDecl;
rel MTokenComponent.Token -> TokenComponent; rel MTokenComponent.Token -> TokenComponent;
rel MContainment.Type -> TypeDecl; rel MContainment.Type -> MTypeDecl;
rel MContainment.Component -> TypeDecl; rel MContainment.Component -> MTypeDecl;
rel MRelation.Left -> TypeDecl; rel MRelation.Left -> MTypeDecl;
rel MRelation.Right -> TypeDecl; rel MRelation.Right -> MTypeDecl;
rel MInheritance.SuperClass -> TypeDecl; rel MTypeDecl.SuperClass? -> MTypeDecl;
rel MInheritance.SubClass -> TypeDecl;
aspect AspectGeneration {
syn String Grammar2Uml.generateAspect() = toMustache().generateAspect();
syn String MGrammar2Uml.generateAspect() {
StringBuilder sb = new StringBuilder();
com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
@Override
public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
@Override
protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
}
return wrapper;
}
};
}
};
com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
// mf.setObjectHandler(roh);
com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache");
m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
return sb.toString();
}
public class AppendableWriter extends java.io.Writer {
private final StringBuilder sb;
public AppendableWriter(StringBuilder sb) {
this.sb = sb;
}
@Override
public void write(char[] chars, int off, int len) {
sb.append(chars, off, len);
}
@Override
public void write(String str) {
sb.append(str);
}
@Override
public void flush() {}
@Override
public void close() {}
}
}
...@@ -26,34 +26,32 @@ aspect AttributesForMustache { ...@@ -26,34 +26,32 @@ aspect AttributesForMustache {
eq MTypeDecl.getInnerTokenComponent(int i).isLast() = i == getNumInnerTokenComponent() - 1; eq MTypeDecl.getInnerTokenComponent(int i).isLast() = i == getNumInnerTokenComponent() - 1;
syn boolean MTypeDecl.isAbstract() = getType().getAbstract(); syn boolean MTypeDecl.isAbstract() = getType().getAbstract();
syn String MTypeDecl.name() = getType().getName();
syn String MTypeDecl.backgroundColor() = getStyle().getBackgroundColor();
// --- MTokenComponent --- // --- MTokenComponent ---
syn String MTokenComponent.name() = getToken().getName(); syn String MTokenComponent.name() = getToken().getName();
syn String MTokenComponent.type() = getToken().hasJavaTypeUse() ? getToken().getJavaTypeUse().getName() : "String";
inh boolean MTokenComponent.isFirst(); inh boolean MTokenComponent.isFirst();
inh boolean MTokenComponent.isLast(); inh boolean MTokenComponent.isLast();
// --- MContainment --- // --- MContainment ---
syn String MContainment.typeName() = getType().getName(); syn String MContainment.typeName() = "\"" + getType().getName() + "\"";
syn String MContainment.componentName() = getComponent().getName(); syn String MContainment.componentName() = "\"" + getComponent().getName() + "\"";
syn String MContainment.modifier(); syn String MContainment.modifier();
eq MSingleContainment.modifier() = "\"1\""; eq MSingleContainment.modifier() = "\"1\"";
eq MOptContainment.modifier() = "\"0 .. 1\""; eq MOptContainment.modifier() = "\"0 .. 1\"";
eq MListContainment.modifier() = "\"*\""; eq MListContainment.modifier() = "\"*\"";
// --- MRelation --- // --- MRelation ---
syn String MRelation.leftName() = getLeft().getName(); syn String MRelation.leftName() = "\"" + getLeft().getName() + "\"";
syn String MRelation.rightName() = getRight().getName(); syn String MRelation.rightName() = "\"" + getRight().getName() + "\"";
syn boolean MRelation.isBidirectional() = getBidirectional(); syn boolean MRelation.isBidirectional() = getBidirectional();
// syn String MRelation.modifier(); // syn String MRelation.modifier();
// eq MSingleRelation.modifier() = "\"1\""; // eq MSingleRelation.modifier() = "\"1\"";
// eq MOptRelation.modifier() = "\"0 .. 1\""; // eq MOptRelation.modifier() = "\"0 .. 1\"";
// eq MListRelation.modifier() = "\"*\""; // eq MListRelation.modifier() = "\"*\"";
// --- MInheritance ---
syn String MInheritance.superClassName() = getSuperClass().getName();
syn String MInheritance.subClassName() = getSubClass().getName();
// --- toMContainment --- // --- toMContainment ---
syn MContainment TypeComponent.toMContainment(); syn MContainment TypeComponent.toMContainment();
eq NormalComponent.toMContainment() = new MSingleContainment(); eq NormalComponent.toMContainment() = new MSingleContainment();
...@@ -85,6 +83,10 @@ aspect AttributesForMustache { ...@@ -85,6 +83,10 @@ aspect AttributesForMustache {
throw new RuntimeException("UnnamedRole cannot be converted to MRelation"); throw new RuntimeException("UnnamedRole cannot be converted to MRelation");
} }
private Style TypeDecl.createDefaultStyle() {
return new Style().setBackgroundColor("white");
}
// --- toMustache --- // --- toMustache ---
syn lazy MGrammar2Uml Grammar2Uml.toMustache() { syn lazy MGrammar2Uml Grammar2Uml.toMustache() {
MGrammar2Uml result = new MGrammar2Uml(); MGrammar2Uml result = new MGrammar2Uml();
...@@ -100,34 +102,28 @@ aspect AttributesForMustache { ...@@ -100,34 +102,28 @@ aspect AttributesForMustache {
if (component.isTypeComponent()) { if (component.isTypeComponent()) {
TypeComponent typeComponent = component.asTypeComponent(); TypeComponent typeComponent = component.asTypeComponent();
MContainment containment = typeComponent.toMContainment(); MContainment containment = typeComponent.toMContainment();
containment.setType(typeDecl); containment.setType(typeDecl.toMustache());
containment.setComponent(component.asTypeComponent().getTypeDecl()); containment.setComponent(component.asTypeComponent().getTypeDecl().toMustache());
if (!component.getName().isEmpty() && !component.getName().equals(component.asTypeComponent().getTypeDecl().getName())) { if (!component.getName().isEmpty() && !component.getName().equals(component.asTypeComponent().getTypeDecl().getName())) {
containment.setLabel(component.getName()); containment.setLabel(component.getName());
} }
result.addContainment(containment); result.addContainment(containment);
} }
} }
if (typeDecl.hasSuperType()) {
MInheritance inheritance = new MInheritance();
inheritance.setSuperClass(typeDecl.getSuperType());
inheritance.setSubClass(typeDecl);
result.addInheritance(inheritance);
}
} }
for (Relation relation : getProgram().relations()) { for (Relation relation : getProgram().relations()) {
if (relation.isDirectedRelation()) { if (relation.isDirectedRelation()) {
DirectedRelation directedRelation = relation.asDirectedRelation(); DirectedRelation directedRelation = relation.asDirectedRelation();
MRelation mRelation = directedRelation.toMRelation(); MRelation mRelation = directedRelation.toMRelation();
mRelation.setLeft(directedRelation.getSource().getType()); mRelation.setLeft(directedRelation.getSource().getType().toMustache());
mRelation.setRight(directedRelation.getTarget().getType()); mRelation.setRight(directedRelation.getTarget().getType().toMustache());
mRelation.setLabel(directedRelation.getSource().getName()); mRelation.setLabel(directedRelation.getSource().getName());
result.addRelation(mRelation); result.addRelation(mRelation);
} else { } else {
BidirectionalRelation bidiRelation = relation.asBidirectionalRelation(); BidirectionalRelation bidiRelation = relation.asBidirectionalRelation();
MRelation mRelation = bidiRelation.toMRelation(); MRelation mRelation = bidiRelation.toMRelation();
mRelation.setLeft(bidiRelation.getLeft().getType()); mRelation.setLeft(bidiRelation.getLeft().getType().toMustache());
mRelation.setRight(bidiRelation.getRight().getType()); mRelation.setRight(bidiRelation.getRight().getType().toMustache());
// mRelation.setLabel(bidiRelation.getSource().getName()); // mRelation.setLabel(bidiRelation.getSource().getName());
result.addRelation(mRelation); result.addRelation(mRelation);
} }
...@@ -152,6 +148,19 @@ aspect AttributesForMustache { ...@@ -152,6 +148,19 @@ aspect AttributesForMustache {
result.addInnerTokenComponent(component.asTokenComponent().toMustache()); result.addInnerTokenComponent(component.asTokenComponent().toMustache());
} }
} }
result.setName(this.getName());
Style style = createDefaultStyle();
grammar2uml().getStyleDefinition().accept(this, style);
result.setStyle(style);
if (this.hasSuperType()) {
Style parentStyle = this.getSuperType().toMustache().getStyle();
if (parentStyle.getInlineAsSuperType()) {
result.setName(getName() + ":" + this.getSuperType().getName());
} else {
result.setSuperClass(this.getSuperType().toMustache());
}
}
return result; return result;
} }
...@@ -162,54 +171,3 @@ aspect AttributesForMustache { ...@@ -162,54 +171,3 @@ aspect AttributesForMustache {
} }
} }
aspect AspectGeneration {
syn String Grammar2Uml.generateAspect() = toMustache().generateAspect();
syn String MGrammar2Uml.generateAspect() {
StringBuilder sb = new StringBuilder();
com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
@Override
public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
@Override
protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
}
return wrapper;
}
};
}
};
com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
// mf.setObjectHandler(roh);
com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache");
m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
return sb.toString();
}
public class AppendableWriter extends java.io.Writer {
private final StringBuilder sb;
public AppendableWriter(StringBuilder sb) {
this.sb = sb;
}
@Override
public void write(char[] chars, int off, int len) {
sb.append(chars, off, len);
}
@Override
public void write(String str) {
sb.append(str);
}
@Override
public void flush() {}
@Override
public void close() {}
}
}
package de.tudresden.inf.st.jastadd.grammar2uml.compiler; package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
import beaver.Parser; import beaver.Parser;
import de.tudresden.inf.st.jastadd.grammar2uml.ast.ErrorMessage;
import de.tudresden.inf.st.jastadd.grammar2uml.ast.Grammar2Uml; import de.tudresden.inf.st.jastadd.grammar2uml.ast.Grammar2Uml;
import de.tudresden.inf.st.jastadd.grammar2uml.ast.GrammarFile;
import de.tudresden.inf.st.jastadd.grammar2uml.ast.Program;
import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.FileUtils;
import net.sourceforge.plantuml.SourceStringReader;
import org.jastadd.option.BooleanOption; import org.jastadd.option.BooleanOption;
import org.jastadd.option.ValueOption; import org.jastadd.option.ValueOption;
import org.jastadd.relast.compiler.AbstractCompiler; import org.jastadd.relast.compiler.AbstractCompiler;
...@@ -19,13 +12,13 @@ import org.jastadd.relast.compiler.CompilerException; ...@@ -19,13 +12,13 @@ import org.jastadd.relast.compiler.CompilerException;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class Compiler extends AbstractCompiler { public class Compiler extends AbstractCompiler {
private final Grammar2UmlProcessor processor;
private ValueOption optionOutputFile; private ValueOption optionOutputFile;
private ValueOption optionInputGrammar2Uml; private ValueOption optionInputGrammar2Uml;
private BooleanOption optionDefaultFolders; private BooleanOption optionDefaultFolders;
...@@ -35,6 +28,7 @@ public class Compiler extends AbstractCompiler { ...@@ -35,6 +28,7 @@ public class Compiler extends AbstractCompiler {
public Compiler() { public Compiler() {
super("grammar2uml" , false); super("grammar2uml" , false);
processor = new Grammar2UmlProcessor();
} }
/** /**
...@@ -66,10 +60,6 @@ public class Compiler extends AbstractCompiler { ...@@ -66,10 +60,6 @@ public class Compiler extends AbstractCompiler {
} }
} }
private void printMessage(String message) {
System.out.println(message);
}
protected void initOptions() { protected void initOptions() {
optionOutputFile = addOption( optionOutputFile = addOption(
new ValueOption("output" , "target file to be generated.") new ValueOption("output" , "target file to be generated.")
...@@ -94,52 +84,6 @@ public class Compiler extends AbstractCompiler { ...@@ -94,52 +84,6 @@ public class Compiler extends AbstractCompiler {
.defaultValue(false)); .defaultValue(false));
} }
private Grammar2Uml parseProgram() throws CompilerException {
Program program = new Program();
Grammar2Uml grammar2Uml;
for (String inputGrammarFileName : getConfiguration().getFiles()) {
printMessage("Parsing " + inputGrammarFileName);
GrammarFile inputGrammar;
try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) {
Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
Grammar2UmlParser parser = new Grammar2UmlParser();
inputGrammar = (GrammarFile) parser.parse(scanner);
if (optionVerbose.value()) {
inputGrammar.dumpTree(System.out);
}
program.addGrammarFile(inputGrammar);
inputGrammar.setFileName(inputGrammarFileName);
} catch (IOException | Parser.Exception e) {
throw new CompilerException("Could not parse grammar file " + inputGrammarFileName, e);
}
}
if (optionInputGrammar2Uml.isMatched()) {
String inputGrammar2UmlFileName = optionInputGrammar2Uml.value();
try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammar2UmlFileName))) {
Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
Grammar2UmlParser parser = new Grammar2UmlParser();
grammar2Uml = (Grammar2Uml) parser.parse(scanner, Grammar2UmlParser.AltGoals.grammar2uml);
grammar2Uml.setFileName(inputGrammar2UmlFileName);
} catch (IOException | Parser.Exception e) {
throw new CompilerException("Could not parse grammar2uml file " + inputGrammar2UmlFileName, e);
}
} else {
// no special setting given
grammar2Uml = new Grammar2Uml();
grammar2Uml.setFileName("<none>");
}
grammar2Uml.setProgram(program);
grammar2Uml.treeResolveAll();
if (optionDefaultFolders.value()) {
for (GrammarFile grammarFile : program.getGrammarFileList()) {
grammar2Uml.addFolder(grammarFile.defaultFolder());
}
}
return grammar2Uml;
}
@Override @Override
protected int compile() throws CompilerException { protected int compile() throws CompilerException {
if (optionVersion.value()) { if (optionVersion.value()) {
...@@ -151,71 +95,28 @@ public class Compiler extends AbstractCompiler { ...@@ -151,71 +95,28 @@ public class Compiler extends AbstractCompiler {
return 0; return 0;
} }
printMessage("Running grammar2uml " + readVersion()); System.out.println("Running grammar2uml " + readVersion());
Path destination = getDestinationPath(); getConfiguration().getFiles().forEach(processor::addGrammar);
Path parent = Paths.get(optionOutputFile.value()).toAbsolutePath().getParent();
try {
Files.createDirectories(parent);
} catch (IOException e) {
throw new CompilerException("Error creating output dir " + parent, e);
}
if (getConfiguration().getFiles().isEmpty()) {
throw new CompilerException("No input grammars specified!");
}
Grammar2Uml grammar2uml = parseProgram();
if (!grammar2uml.errors().isEmpty()) { if (optionInputGrammar2Uml.isMatched()) {
System.err.println("Errors:"); String inputGrammar2UmlFileName = optionInputGrammar2Uml.value();
for (ErrorMessage e : grammar2uml.errors()) { try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammar2UmlFileName))) {
System.err.println(e); Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
Grammar2UmlParser parser = new Grammar2UmlParser();
Grammar2Uml grammar2Uml = (Grammar2Uml) parser.parse(scanner, Grammar2UmlParser.AltGoals.grammar2uml);
processor.addPostProcessing(g -> g.setFileName(inputGrammar2UmlFileName));
grammar2Uml.getFolderList().forEach(processor::addFolder);
} catch (IOException | Parser.Exception e) {
throw new CompilerException("Could not parse grammar2uml file " + inputGrammar2UmlFileName, e);
} }
System.exit(1);
} }
printMessage("Writing output file " + destination); processor.setUseDefaultFolders(optionDefaultFolders.value())
.setVerbose(optionVerbose.value())
String sourceCode = grammar2uml.generateAspect(); .writeFile(Paths.get(optionOutputFile.value()).toAbsolutePath());
//
String extension = fileExtensionOf(destination).toUpperCase();
FileFormatOption plantUmlOption = null;
switch (extension) {
case "MD":
try {
Files.writeString(destination, sourceCode);
} catch (Exception e) {
throw new CompilerException("Could not write to file " + destination, e);
}
break;
case "HTML":
case "PNG":
case "PDF":
case "SVG":
plantUmlOption = new FileFormatOption(FileFormat.valueOf(extension));
break;
}
if (plantUmlOption != null) {
try {
SourceStringReader reader = new SourceStringReader(sourceCode);
reader.outputImage(Files.newOutputStream(destination), plantUmlOption);
} catch (Exception e) {
throw new CompilerException("Could not write to file " + destination, e);
}
}
return 0; return 0;
} }
private String fileExtensionOf(Path path) {
String fileName = path.toFile().getName();
int lastIndexOfDot = fileName.lastIndexOf(".");
return fileName.substring(lastIndexOfDot + 1);
}
private Path getDestinationPath() {
return Paths.get(optionOutputFile.value()).toAbsolutePath();
}
} }
package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
import beaver.Parser;
import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* Producing images from grammars.
*
* @author rschoene - Initial contribution
*/
public class Grammar2UmlProcessor {
private final List<String> inputGrammarFiles = new ArrayList<>();
private boolean verbose;
private boolean useDefaultFolders;
private final List<Folder> folders = new ArrayList<>();
private String generatedAspect = null;
private final List<Consumer<Grammar2Uml>> callbacks = new ArrayList<>();
private StyleDefinition styleDefinition = (typeDecl, style) -> {};
public Grammar2UmlProcessor addGrammar(String... inputGrammarFiles) {
Collections.addAll(this.inputGrammarFiles, inputGrammarFiles);
return this;
}
public Grammar2UmlProcessor setVerbose(boolean value) {
this.verbose = value;
return this;
}
public Grammar2UmlProcessor addFolder(Folder... folders) {
Collections.addAll(this.folders, folders);
return this;
}
public Grammar2UmlProcessor setUseDefaultFolders(boolean value) {
useDefaultFolders = value;
return this;
}
public Grammar2UmlProcessor addPostProcessing(Consumer<Grammar2Uml> callback) {
this.callbacks.add(callback);
return this;
}
public Grammar2UmlProcessor setStyleDefinition(StyleDefinition definition) {
this.styleDefinition = definition;
return this;
}
public void writeFile(Path destination) throws CompilerException {
build();
Path parent = destination.toAbsolutePath().getParent();
try {
Files.createDirectories(parent);
} catch (IOException e) {
throw new CompilerException("Error creating output dir " + parent, e);
}
if (inputGrammarFiles.isEmpty()) {
throw new CompilerException("No input grammars specified!");
}
printMessage("Writing output file " + destination);
String extension = fileExtensionOf(destination).toUpperCase();
FileFormatOption plantUmlOption = null;
switch (extension) {
case "MD":
try {
Files.writeString(destination, generatedAspect);
} catch (Exception e) {
throw new CompilerException("Could not write to file " + destination, e);
}
break;
case "HTML":
case "PNG":
case "PDF":
case "SVG":
plantUmlOption = new FileFormatOption(FileFormat.valueOf(extension));
break;
}
if (plantUmlOption != null) {
try {
SourceStringReader reader = new SourceStringReader(generatedAspect);
reader.outputImage(Files.newOutputStream(destination), plantUmlOption);
} catch (Exception e) {
throw new CompilerException("Could not write to file " + destination, e);
}
}
}
private void build() throws CompilerException {
if (generatedAspect != null) {
return; // already built
}
Program program = new Program();
Grammar2Uml grammar2uml = new Grammar2Uml();
for (String inputGrammarFileName : inputGrammarFiles) {
printMessage("Parsing " + inputGrammarFileName);
GrammarFile inputGrammar;
try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) {
Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader);
Grammar2UmlParser parser = new Grammar2UmlParser();
inputGrammar = (GrammarFile) parser.parse(scanner);
if (verbose) {
inputGrammar.dumpTree(System.out);
}
program.addGrammarFile(inputGrammar);
inputGrammar.setFileName(inputGrammarFileName);
} catch (IOException | Parser.Exception e) {
throw new CompilerException("Could not parse grammar file " + inputGrammarFileName, e);
}
}
if (useDefaultFolders) {
for (GrammarFile grammarFile : program.getGrammarFileList()) {
grammar2uml.addFolder(grammarFile.defaultFolder());
}
}
for (Folder folder : folders) {
grammar2uml.addFolder(folder);
}
grammar2uml.setStyleDefinition(styleDefinition);
callbacks.forEach(consumer -> consumer.accept(grammar2uml));
grammar2uml.setProgram(program);
grammar2uml.treeResolveAll();
if (!grammar2uml.errors().isEmpty()) {
System.err.println("Errors:");
for (ErrorMessage e : grammar2uml.errors()) {
System.err.println(e);
}
throw new CompilerException("Exiting because of previous errors.");
}
generatedAspect = grammar2uml.generateAspect();
}
private void printMessage(String message) {
if (verbose) {
System.out.println(message);
}
}
private String fileExtensionOf(Path path) {
String fileName = path.toFile().getName();
int lastIndexOfDot = fileName.lastIndexOf(".");
return fileName.substring(lastIndexOfDot + 1);
}
}
...@@ -4,6 +4,7 @@ import beaver.Parser; ...@@ -4,6 +4,7 @@ import beaver.Parser;
import de.tudresden.inf.st.jastadd.grammar2uml.ast.*; import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
import org.jastadd.relast.compiler.CompilerException;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -16,11 +17,32 @@ import java.nio.file.Paths; ...@@ -16,11 +17,32 @@ import java.nio.file.Paths;
* *
* @author rschoene - Initial contribution * @author rschoene - Initial contribution
*/ */
public class Grammar2UmlMain { public class SimpleMain {
public static void main(String[] args) { public static void main(String[] args) throws Exception {
// testing(); // testing();
processManualAST(); // processManualAST();
useAPI();
}
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",
"src/gen/jastadd-sources/relast.preprocessor/RelAst.relast")
.setStyleDefinition((typeDecl, style) -> {
switch (typeDecl.getName()) {
case "Component":
case "Comment":
style.setInlineAsSuperType(true);
break;
case "TypeDecl":
style.setBackgroundColor("blue\\9932CC");
}
})
.setUseDefaultFolders(true);
processor.writeFile(Paths.get("api.md"));
processor.writeFile(Paths.get("api.png"));
} }
public static Grammar2Uml createManualAST() { public static Grammar2Uml createManualAST() {
......
{{typeName}} *-- {{{modifier}}} {{componentName}} {{#Label}}: {{Label}}{{/Label}} {{{typeName}}} *-- {{{modifier}}} {{{componentName}}} {{#Label}}: {{Label}}{{/Label}}
{{superClassName}} <|-- {{subClassName}}
{{leftName}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}--> {{{rightModifier}}} {{rightName}} {{#Label}}: {{Label}}{{/Label}} {{{leftName}}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}-[norank]-> {{{rightModifier}}} {{{rightName}}} {{#Label}}: {{Label}}{{/Label}}
{{=<% %>=}}<%#isAbstract%>abstract <%/isAbstract%>class <%name%><%#InnerTokenComponents%><%#first%> { {{#isAbstract}}abstract {{/isAbstract}}class "{{{Name}}}"{{#backgroundColor}} #{{{backgroundColor}}}{{/backgroundColor}}{{#InnerTokenComponents}}{{#first}} {
<%/first%> {{/first}}
<%name%> {{{type}}} {{{name}}}
<%#last%> {{#last}}
}<%/last%><%/InnerTokenComponents%><%={{ }}=%> }{{/last}}{{/InnerTokenComponents}}
{{#hasSuperClass}}{{#SuperClass}}"{{{Name}}}"{{/SuperClass}} <|-- "{{{Name}}}"{{/hasSuperClass}}
...@@ -6,6 +6,5 @@ hide methods ...@@ -6,6 +6,5 @@ hide methods
{{#OtherTypes}}{{> TypeDecl}}{{/OtherTypes}} {{#OtherTypes}}{{> TypeDecl}}{{/OtherTypes}}
{{#Containments}}{{> Containment}}{{/Containments}} {{#Containments}}{{> Containment}}{{/Containments}}
{{#Relations}}{{> Relation}}{{/Relations}} {{#Relations}}{{> Relation}}{{/Relations}}
{{#Inheritances}}{{> Inheritance}}{{/Inheritances}}
@enduml @enduml
``` ```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment