diff --git a/dumpAst2uml/build.gradle b/dumpAst2uml/build.gradle index 7690a02f55330f0f0c199bbf94cdfcd285912e72..048b27dc3cb0d93cdfc2d580683197fa4e68bde3 100644 --- a/dumpAst2uml/build.gradle +++ b/dumpAst2uml/build.gradle @@ -19,7 +19,8 @@ buildscript { dependencies { implementation project(':grammar2uml') // just to test SimpleMain - implementation project(':relast.preprocessor') + implementation fileTree(include: ['plantuml.jar'], dir: '../libs') +// implementation project(':relast.preprocessor') implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: '0.9.6' implementation group: 'org.apache.logging.log4j', name: 'log4j-jul', version: '2.11.2' @@ -39,7 +40,7 @@ try { version = oldProps['version'] } catch (e) { // this happens, if either the properties file is not present, or cannot be read from - throw new GradleException("File ${versionFile} not found or unreadable. Aborting.") + throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e) } task newVersion() { @@ -73,116 +74,80 @@ test { maxHeapSize = '1G' } -//task relast(type: JavaExec) { -// group = 'Build' -// main = "-jar" -// -// doFirst { -// delete "src/gen/jastadd/*.ast" -// delete "src/gen/jastadd/Grammar2Uml.jadd" -// delete "src/gen/jastadd/Grammar2UmlRefResolver.jadd" -// delete "src/gen/jastadd/Grammar2UmlResolverStubs.jrag" -// mkdir "src/gen/jastadd/" -// } -// -// args = [ -// "../libs/relast.jar", -// "../relast.preprocessor/src/main/jastadd/RelAst.relast", -// "./src/main/jastadd/Grammar2Uml.relast", +File dumpAstGrammar = file('./src/main/jastadd/DumpAst.relast') + +task relast(type: JavaExec) { + group = 'Build' + main = "-jar" + + doFirst { + delete "src/gen/jastadd/*.ast" + delete "src/gen/jastadd/DumpAst2.jadd" + delete "src/gen/jastadd/DumpAstlRefResolver.jadd" + delete "src/gen/jastadd/DumpAstlResolverStubs.jrag" + mkdir "src/gen/jastadd/" + } + + args = [ + "../libs/relast.jar", + dumpAstGrammar, // "./src/main/jastadd/MustacheNodes.relast", -// "--listClass=java.util.ArrayList", -// "--jastAddList=JastAddList", -// "--useJastAddNames", -// "--file", -// "--resolverHelper", -// "--grammarName=./src/gen/jastadd/Grammar2Uml" -// ] -// -//// inputs.files file("../libs/relast.jar"), -//// file("../relast.preprocessor/src/main/jastadd/RelAST.relast"), -//// file("./src/main/jastadd/Grammar2Uml.relast") -//// file("./src/main/jastadd/MustacheNodes.relast") -//// outputs.files file("./src/gen/jastadd/Grammar2Uml.ast"), -//// file("./src/gen/jastadd/Grammar2Uml.jadd"), -//// file("./src/gen/jastadd/Grammar2UmlRefResolver.jadd"), -//// file('./src/gen/jastadd/Grammar2UmlResolverStubs.jrag') -//} -// -//jastadd { -// configureModuleBuild() -// modules { -// //noinspection GroovyAssignabilityCheck -// module("Grammar2Uml") { -// -// java { -// basedir ".." -// include "relast.preprocessor/main/**/*.java" -// include "relast.preprocessor/gen/**/*.java" -// include "dumpAst2uml/src/main/**/*.java" -// include "dumpAst2uml/src/gen/**/*.java" -// } -// -// jastadd { -// basedir ".." -// include "relast.preprocessor/src/main/jastadd/**/*.ast" -// include "relast.preprocessor/src/main/jastadd/**/*.jadd" -// include "relast.preprocessor/src/main/jastadd/**/*.jrag" -// include "dumpAst2uml/src/main/jastadd/**/*.ast" -// include "dumpAst2uml/src/main/jastadd/**/*.jadd" -// include "dumpAst2uml/src/main/jastadd/**/*.jrag" -// include "dumpAst2uml/src/gen/jastadd/**/*.ast" -// include "dumpAst2uml/src/gen/jastadd/**/*.jadd" -// include "dumpAst2uml/src/gen/jastadd/**/*.jrag" -// } -// -// scanner { -// basedir ".." -// include "dumpAst2uml/src/main/jastadd/scanner/Header.flex", [-5] -// include "relast.preprocessor/src/main/jastadd/scanner/Preamble.flex", [-4] -// include "relast.preprocessor/src/main/jastadd/scanner/Macros.flex", [-3] -// include "dumpAst2uml/src/main/jastadd/scanner/Macros.flex", [-3] -// include "relast.preprocessor/src/main/jastadd/scanner/RulesPreamble.flex", [-2] -// include "dumpAst2uml/src/main/jastadd/scanner/MappingContent.flex", [-1] -// include "dumpAst2uml/src/main/jastadd/scanner/Keywords.flex" -// include "relast.preprocessor/src/main/jastadd/scanner/Keywords.flex" -// include "relast.preprocessor/src/main/jastadd/scanner/Symbols.flex", [1] -// include "relast.preprocessor/src/main/jastadd/scanner/RulesPostamble.flex", [2] -// } -// -// parser { -// basedir ".." -// include "dumpAst2uml/src/main/jastadd/parser/Preamble.parser" -// include "relast.preprocessor/src/main/jastadd/parser/RelAst.parser" -// include "dumpAst2uml/src/main/jastadd/parser/Grammar2Uml.parser" -// } -// } -// } -// -// cleanGen.doFirst { -// delete "src/gen/java/org" -// delete "src/gen-res/BuildInfo.properties" -// } -// -// preprocessParser.doFirst { -// -// args += ["--no-beaver-symbol"] -// -// } -// -// module = "Grammar2Uml" -// -// astPackage = 'org.jastadd.dumpAst2uml.ast' -// -// parser.name = 'Grammar2UmlParser' -// -// genDir = 'src/gen/java' -// -// buildInfoDir = 'src/gen-res' -// -// scanner.genDir = "src/gen/java/org/jastadd/dumpAst2uml/scanner" -// parser.genDir = "src/gen/java/org/jastadd/dumpAst2uml/parser" -// -// jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] -//} -// -//generateAst.dependsOn relast + "--listClass=java.util.ArrayList", + "--jastAddList=JastAddList", + "--useJastAddNames", + "--file", + "--resolverHelper", + "--grammarName=./src/gen/jastadd/DumpAst2" + ] + + inputs.files(file("../libs/relast.jar"), + dumpAstGrammar) + outputs.files(file("./src/gen/jastadd/DumpAst2.ast"), + file("./src/gen/jastadd/DumpAst.jadd"), + file("./src/gen/jastadd/DumpAstRefResolver.jadd"), + file('./src/gen/jastadd/DumpAstResolverStubs.jrag')) +} + +jastadd { + configureModuleBuild() + modules { + //noinspection GroovyAssignabilityCheck + module("DumpAst2Uml") { + + java { + basedir ".." + include "dumpAst2uml/src/main/**/*.java" + include "dumpAst2uml/src/gen/**/*.java" + } + + jastadd { + basedir ".." + include "dumpAst2uml/src/main/jastadd/**/*.ast" + include "dumpAst2uml/src/main/jastadd/**/*.jadd" + include "dumpAst2uml/src/main/jastadd/**/*.jrag" + include "dumpAst2uml/src/gen/jastadd/**/*.ast" + include "dumpAst2uml/src/gen/jastadd/**/*.jadd" + include "dumpAst2uml/src/gen/jastadd/**/*.jrag" + } + } + } + + cleanGen.doFirst { + delete "src/gen/java/org" + delete "src/gen-res/BuildInfo.properties" + } + + preprocessParser.doFirst { + + args += ["--no-beaver-symbol"] + + } + + module = "DumpAst2Uml" + astPackage = 'org.jastadd.dumpAst2uml.ast' + genDir = 'src/gen/java' + buildInfoDir = 'src/gen-res' + jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] +} + +generateAst.dependsOn relast diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst2uml/src/main/jastadd/DumpAst.relast new file mode 100644 index 0000000000000000000000000000000000000000..1d94dc6875215d74e9167d54a36154d4461bbbd3 --- /dev/null +++ b/dumpAst2uml/src/main/jastadd/DumpAst.relast @@ -0,0 +1,23 @@ +DumpAst ::= DumpNode* <PackageName> ; +DumpNode ::= <Name> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ; +InnerDumpNode ; +rel InnerDumpNode.DumpNode -> DumpNode ; + +abstract DumpChildNode ::= <Name> ; +DumpNormalChildNode : DumpChildNode ; +rel DumpNormalChildNode.DumpNode -> DumpNode ; +DumpListChildNode : DumpChildNode ::= InnerDumpNode* ; +//DumpOptChildNode : DumpChildNode ; +//rel DumpOptChildNode.DumpNode? -> DumpNode ; + +abstract DumpToken ::= <Name> ; +DumpReferenceToken : DumpToken ; +rel DumpReferenceToken.Value -> DumpNode ; +DumpValueToken : DumpToken ::= <Value:Object> ; + +abstract DumpRelation ::= <Name> <Bidirectional:boolean> ; +DumpNormalRelation : DumpRelation ; +rel DumpNormalRelation.DumpNode -> DumpNode ; +DumpListRelation : DumpRelation ::= InnerDumpNode* ; +//DumpOptRelation : DumpRelation ; +//rel DumpOptRelation.DumpNode? -> DumpNode ; diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst2uml/src/main/jastadd/Generation.jadd new file mode 100644 index 0000000000000000000000000000000000000000..7dd3c516032df66b344676cd2cde3afa572ed1c0 --- /dev/null +++ b/dumpAst2uml/src/main/jastadd/Generation.jadd @@ -0,0 +1,203 @@ +aspect Generation { + public class Dumper { + public static DumpBuilder read(Object obj) { + return new DumpBuilder(obj); + } + } + class DumpBuilder { + private Object target; + private String packageName; + private DumpAst result; + protected DumpBuilder(Object target) { + this.target = target; + } + public DumpBuilder ignoreTypes(String... regexes) { + // TODO + return this; + } + public DumpBuilder skinParam(String option) { + // TODO + return this; + } + protected DumpAst build() { + if (result == null) { + result = new DumpAst(); + result.setPackageName(this.packageName == null ? this.target.getClass().getPackage().getName() : this.packageName); + try { + result.transform(new TransformationTransferInformation(), this.target); + } catch (java.lang.reflect.InvocationTargetException e) { + throw new RuntimeException("Could not transform :(", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Could not transform :(", e); + } + } + return result; + } + public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException { + String content = build().toPlantUml(); + try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) { + writer.write(content); + } + return this; + } + public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException { + String content = build().toPlantUml(); + net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content); + reader.outputImage(java.nio.file.Files.newOutputStream(destination)); + return this; + } + } + // --- transform --- (need to be a method, because it alters the AST while traversing the object structure) + // maybe return type is unncessary + protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj) throws java.lang.reflect.InvocationTargetException, IllegalAccessException { + if (obj == null) { + return null; + } + if (tti.transformed.containsKey(obj)) { + return tti.transformed.get(obj); + } + DumpNode node = new DumpNode(); + node.setObject(obj); + tti.transformed.put(obj, node); + this.addDumpNode(node); + if (node.isAstNode()) { + // only caching node.analyseClass does not help, since we want to do this only once per class of a node + ClassAnalysisResult car = tti.classAnalysisResults.computeIfAbsent(node.getClass(), clz -> node.analyseClass()); + // -- singleChild -- + for (java.lang.reflect.Method method : car.singleChildMethods) { + Object target = method.invoke(obj); + if (target != null) { + DumpNormalChildNode normalChild = new DumpNormalChildNode(); + normalChild.setName(car.names.get(method)); + normalChild.setDumpNode(transform(tti, target)); + node.addDumpChildNode(normalChild); + } + } + // -- listChild -- + for (java.lang.reflect.Method method : car.listChildMethods) { + Iterable<?> targetList = (Iterable<?>) method.invoke(obj); + DumpListChildNode listChild = new DumpNormalChildNode(); + listChild.setName(car.names.get(method)); + for (Object target : targetList) { + if (target != null) { + listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target))); + } + } + node.addDumpChildNode(listChild); + } + // -- singleRelation -- + for (java.lang.reflect.Method method : car.singleRelationMethods) { + Object target = method.invoke(obj); + if (target != null) { + DumpNormalRelation normalChild = new DumpNormalRelation(); + normalChild.setName(car.names.get(method)); + normalChild.setDumpNode(transform(tti, target)); + node.addDumpRelation(normalChild); + } + } + // -- listChild -- + for (java.lang.reflect.Method method : car.listChildMethods) { + Iterable<?> targetList = (Iterable<?>) method.invoke(obj); + DumpListRelation listChild = new DumpNormalRelation(); + listChild.setName(car.names.get(method)); + for (Object target : targetList) { + if (target != null) { + listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target))); + } + } + node.addDumpRelation(listChild); + } + // -- token -- + for (java.lang.reflect.Method method : car.tokenMethods) { + Object target = method.invoke(obj); + if (target != null) { + DumpNode targetNode = transform(tti, target); + DumpToken token; + if (targetNode.isAstNode()) { + token = new DumpReferenceToken(); + token.setValue(targetNode); + } else { + // maybe ignore empty string values here + token = new DumpValueToken(); + token.setValue(target); + } + token.setName(car.names.get(method)); + node.addDumpToken(token); + } + } + } + return node; + } + + syn ClassAnalysisResult DumpNode.analyseClass() { + // TODO + return null; + } + + syn boolean DumpNode.isAstNode() { + // TODO + return false; + } + + class TransformationTransferInformation { + java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>(); + java.util.Map<Class<?>, ClassAnalysisResult> classAnalysisResults = new java.util.HashMap<>(); + } + + class ClassAnalysisResult { + java.lang.reflect.Method[] singleChildMethods; + java.lang.reflect.Method[] listChildMethods; + java.lang.reflect.Method[] singleRelationMethods; + java.lang.reflect.Method[] listRelationMethods; + java.lang.reflect.Method[] tokenMethods; + java.util.Map<java.lang.reflect.Method, String> names; + } + + syn String DumpAst.toPlantUml() { + 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("dumpAst.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() {} + } +} \ No newline at end of file diff --git a/dumpAst2uml/src/main/jastadd/Navigation.jrag b/dumpAst2uml/src/main/jastadd/Navigation.jrag new file mode 100644 index 0000000000000000000000000000000000000000..708d016d515fa4f95b46a7662adffb38c28067d6 --- /dev/null +++ b/dumpAst2uml/src/main/jastadd/Navigation.jrag @@ -0,0 +1,11 @@ +aspect Navigation { + // --- isList --- + syn boolean DumpChildNode.isList() = false; + eq DumpListChildNode.isList() = true; + syn boolean DumpRelation.isList() = false; + eq DumpListRelation.isList() = true; + + // --- isDumpValueToken --- + syn boolean DumpToken.isDumpValueToken() = false; + eq DumpValueToken.isDumpValueToken() = true; +} \ No newline at end of file diff --git a/dumpAst2uml/src/main/jastadd/Printing.jrag b/dumpAst2uml/src/main/jastadd/Printing.jrag new file mode 100644 index 0000000000000000000000000000000000000000..9a399ba6bfdfd9ec6f5c8e279d6cf6fb39e933c1 --- /dev/null +++ b/dumpAst2uml/src/main/jastadd/Printing.jrag @@ -0,0 +1,23 @@ +aspect Printing { + // --- outerNodeName --- + inh String InnerDumpNode.outerNodeName(); + inh String DumpChildNode.outerNodeName(); + inh String DumpRelation.outerNodeName(); + inh String DumpReferenceToken.outerNodeName(); + eq DumpNode.getChild().outerNodeName() = name(); + + // --- innerNodeName --- + syn String InnerDumpNode.innerNodeName() = getDumpNode().name(); + syn String DumpNormalChildNode.innerNodeName() = getDumpNode().name(); + syn String DumpNormalRelation.innerNodeName() = getDumpNode().name(); + syn String DumpReferenceToken.innerNodeName() = getValue().name(); + + // --- name --- + syn String DumpNode.name() = getName(); // might change in the future + + // --- label --- + syn String DumpChildNode.label() = getName(); + syn String DumpRelation.label() = getName(); + syn String DumpToken.label() = getName(); + +} diff --git a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java index 1ccafbacb5e000da51f5fffb936ef63c6574116b..f6e83dbf0441b9dc2a55a232c9e26ddfccfdd816 100644 --- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java +++ b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java @@ -1,21 +1,25 @@ package org.jastadd.dumpAst2uml.compiler; import beaver.Parser; +import org.jastadd.dumpAst2uml.ast.*; import org.jastadd.grammar2uml.ast.*; import org.jastadd.grammar2uml.parser.Grammar2UmlParser; import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner; -import java.io.BufferedReader; -import java.io.IOException; +import java.io.*; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import static org.jastadd.dumpAst2uml.compiler.SimpleMain.Kind.*; + /** * Testing Relast2Uml without parser. * @@ -24,11 +28,72 @@ import java.util.Set; public class SimpleMain { public static void main(String[] args) { -// testing(); +// printing(); createManualAST(); } private static void createManualAST() { + DumpAst dumpAst = new DumpAst(); + dumpAst.setPackageName("foo"); + DumpNode a = new DumpNode(); + a.setName("a"); + dumpAst.addDumpNode(a); + DumpNode b = new DumpNode(); + b.setName("b"); + dumpAst.addDumpNode(b); + DumpNode c = new DumpNode(); + c.setName("c"); + dumpAst.addDumpNode(c); + DumpNode d = new DumpNode(); + d.setName("d"); + dumpAst.addDumpNode(d); + DumpNode e = new DumpNode(); + e.setName("e"); + dumpAst.addDumpNode(e); + DumpNode f = new DumpNode(); + f.setName("f"); + dumpAst.addDumpNode(f); + DumpNode g = new DumpNode(); + g.setName("g"); + dumpAst.addDumpNode(g); + DumpNode h = new DumpNode(); + h.setName("h"); + dumpAst.addDumpNode(h); + + DumpListChildNode listNode = new DumpListChildNode(); + listNode.setName("list"); + listNode.addInnerDumpNode(new InnerDumpNode(b)); + listNode.addInnerDumpNode(new InnerDumpNode(c)); + a.addDumpChildNode(listNode); + a.addDumpChildNode(new DumpNormalChildNode("opt", d)); + a.addDumpChildNode(new DumpNormalChildNode("normal", e)); + + DumpListRelation listRelation = new DumpListRelation(); + listRelation.setName("listRel"); + listRelation.addInnerDumpNode(new InnerDumpNode(f)); + listRelation.addInnerDumpNode(new InnerDumpNode(g)); + b.addDumpRelation(listRelation); + + c.addDumpRelation(new DumpNormalRelation("relOpt", false, g)); + c.addDumpRelation(new DumpNormalRelation("biRelOpt", true, h)); + + d.addDumpRelation(new DumpNormalRelation("relNormal", false, c)); + + e.addDumpToken(new DumpReferenceToken("ref", h)); + + h.addDumpToken(new DumpValueToken("intValue", 4)); + DumpListRelation biListRelation = new DumpListRelation(); + biListRelation.setName("biRelList"); + biListRelation.setBidirectional(true); + biListRelation.addInnerDumpNode(new InnerDumpNode(f)); + biListRelation.addInnerDumpNode(new InnerDumpNode(g)); + h.addDumpRelation(biListRelation); + + String content = dumpAst.toPlantUml(); + System.out.println(content); + } + + private static void printing() { System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); System.setProperty("mustache.debug", "true"); Grammar2Uml model = new Grammar2Uml(); @@ -48,6 +113,7 @@ public class SimpleMain { } static final String MORE_INDENT = "| "; + static final String AST_NODE_ANNOTATION = "org.jastadd.grammar2uml.ast" + ".ASTNodeAnnotation"; private static void traverseInitial(Object obj) { traverse(obj, new HashSet<>(), ""); @@ -61,75 +127,93 @@ public class SimpleMain { System.out.println(indent + "|> " + obj.toString()); System.out.println(indent + "| isAstNode(obj) = " + isAstNode(obj)); printChildren(obj, seen, indent); - printTokens(obj, seen, indent); +// printTokens(obj, seen, indent); } private static boolean isAstNode(Object obj) { Class<?> clazz = obj.getClass(); for (Constructor<?> constructor : clazz.getConstructors()) { - if (constructor.isAnnotationPresent(ASTNodeAnnotation.Constructor.class)) { + if (hasConstructionAnnotationClass(constructor)) { return true; } } return false; } - static void printChildren(Object obj, Set<Object> seen, String indent) { - Class<?> clazz = obj.getClass(); - for (Method method : clazz.getMethods()) { - final String name; - final String kind; - if (method.isAnnotationPresent(ASTNodeAnnotation.Child.class)) { - name = method.getAnnotation(ASTNodeAnnotation.Child.class).name(); - kind = ""; - } else if (method.isAnnotationPresent(ASTNodeAnnotation.ListChild.class)) { - name = method.getAnnotation(ASTNodeAnnotation.ListChild.class).name(); - kind = "list-"; - } else if (method.isAnnotationPresent(ASTNodeAnnotation.OptChild.class)) { - name = method.getAnnotation(ASTNodeAnnotation.OptChild.class).name(); - kind = "opt-"; - } else { - name = null; - kind = null; + private static boolean hasConstructionAnnotationClass(Constructor<?> constructor) { +// ASTNodeAnnotation.Constructor.class; + for (Annotation annotation : constructor.getAnnotations()) { + String canonicalName = annotation.annotationType().getCanonicalName(); + if (canonicalName.startsWith(AST_NODE_ANNOTATION) && annotation.annotationType().getSimpleName().equals("Constructor")) { + return true; } - if (kind != null) { - try { - Object child = method.invoke(obj); - System.out.println(indent + "| " + kind + "child (" + name + ") = " + child); - traverse(child, seen, indent + MORE_INDENT); - } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); + } + return false; + } + + enum Kind {child, list, opt, tokenOrRelation, token, relation, other} + static class NameAndKind { + String name; + Kind kind; + static NameAndKind of(String name, Kind kind) { + NameAndKind result = new NameAndKind(); + result.name = name; + result.kind = kind; + return result; + } + } + + private static NameAndKind analyzeMethod(Method method) { + for (Annotation annotation : method.getAnnotations()) { + String canonicalName = annotation.annotationType().getCanonicalName(); + if (canonicalName.startsWith(AST_NODE_ANNOTATION)) { + String simpleName = annotation.annotationType().getSimpleName(); + switch (simpleName) { + case "Child": return NameAndKind.of(invokeName(annotation), Kind.child); + case "ListChild": return NameAndKind.of(invokeName(annotation), Kind.list); + case "OptChild": return NameAndKind.of(invokeName(annotation), Kind.opt); + case "Token": return NameAndKind.of(invokeName(annotation), tokenOrRelation); } } } + return NameAndKind.of(null, Kind.other); + } + + private static String invokeName(Annotation annotation) { + try { + return (String) annotation.annotationType().getMethod("name").invoke(annotation); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + return "<error>"; + } } - static void printTokens(Object obj, Set<Object> seen, String indent) { + static void printChildren(Object obj, Set<Object> seen, String indent) { Class<?> clazz = obj.getClass(); for (Method method : clazz.getMethods()) { - if (method.isAnnotationPresent(ASTNodeAnnotation.Token.class)) { - Method methodToInvoke = null; - String name = method.getAnnotation(ASTNodeAnnotation.Token.class).name(); + NameAndKind nameAndKind = analyzeMethod(method); + String name = nameAndKind.name; + Kind kind = nameAndKind.kind; + Method methodToInvoke = method; + if (kind == tokenOrRelation) { // heuristic for relations if (name.startsWith("_impl_")) { try { methodToInvoke = clazz.getMethod("get" + name.substring(6)); name = name.substring(6); + kind = relation; } catch (NoSuchMethodException e) { // this is probably not a relation } } - final String kind; - if (methodToInvoke == null) { - methodToInvoke = method; - kind = "token"; - } else { - kind = "relation"; + if (kind == tokenOrRelation) { + kind = token; } + } + if (kind != other) { try { - Object value = methodToInvoke.invoke(obj); - System.out.println(indent + "| " + kind + " (" + name + ") = " + value); - traverse(value, seen, indent + MORE_INDENT); + Object child = methodToInvoke.invoke(obj); + System.out.println(indent + "| " + kind + " child (" + name + ") = " + child); + traverse(child, seen, indent + MORE_INDENT); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } @@ -137,6 +221,39 @@ public class SimpleMain { } } +// static void printTokens(Object obj, Set<Object> seen, String indent) { +// Class<?> clazz = obj.getClass(); +// for (Method method : clazz.getMethods()) { +// if (method.isAnnotationPresent(ASTNodeAnnotation.Token.class)) { +// Method methodToInvoke = null; +// String name = method.getAnnotation(ASTNodeAnnotation.Token.class).name(); +// // heuristic for relations +// if (name.startsWith("_impl_")) { +// try { +// methodToInvoke = clazz.getMethod("get" + name.substring(6)); +// name = name.substring(6); +// } catch (NoSuchMethodException e) { +// // this is probably not a relation +// } +// } +// final String kind; +// if (methodToInvoke == null) { +// methodToInvoke = method; +// kind = "token"; +// } else { +// kind = "relation"; +// } +// try { +// Object value = methodToInvoke.invoke(obj); +// System.out.println(indent + "| " + kind + " (" + name + ") = " + value); +// traverse(value, seen, indent + MORE_INDENT); +// } catch (IllegalAccessException | InvocationTargetException e) { +// e.printStackTrace(); +// } +// } +// } +// } + private static Program parseProgram(Path path) { try (BufferedReader reader = Files.newBufferedReader(path)) { Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader); diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst2uml/src/main/resources/dumpAst.mustache new file mode 100644 index 0000000000000000000000000000000000000000..ddbc1b11a0448d516319e8ddd9c0ce08a2b38bd0 --- /dev/null +++ b/dumpAst2uml/src/main/resources/dumpAst.mustache @@ -0,0 +1,39 @@ +@startuml +{{#DumpNodes}} +object {{name}} { +{{! tokens }} +{{#DumpTokens}} +{{#isDumpValueToken}} + {{label}} = {{Value}} +{{/isDumpValueToken}} +{{/DumpTokens}} +} +{{#DumpTokens}} +{{^isDumpValueToken}} +{{outerNodeName}} ..> {{innerNodeName}} : {{label}} +{{/isDumpValueToken}} +{{/DumpTokens}} +{{! child nodes }} +{{#DumpChildNodes}} +{{#isList}} +{{#InnerDumpNodes}} +{{outerNodeName}} *-- {{innerNodeName}} : {{label}} +{{/InnerDumpNodes}} +{{/isList}} +{{^isList}} +{{outerNodeName}} *-- {{innerNodeName}} : {{label}} +{{/isList}} +{{/DumpChildNodes}} +{{! relations }} +{{#DumpRelations}} +{{#isList}} +{{#InnerDumpNodes}} +{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} +{{/InnerDumpNodes}} +{{/isList}} +{{^isList}} +{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} +{{/isList}} +{{/DumpRelations}} +{{/DumpNodes}} +@enduml diff --git a/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties b/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties new file mode 100644 index 0000000000000000000000000000000000000000..9e9347a71a9cec4a1fb0a4118eae4835e757cb73 --- /dev/null +++ b/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties @@ -0,0 +1,2 @@ +#Sat Sep 05 18:50:58 CEST 2020 +version=0.2 diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle index 44378a3b612dff552da7c45bafdf0a00b5976f65..578aaa1664d74c94a9d2adecb49cd5a78ae24370 100644 --- a/grammar2uml/build.gradle +++ b/grammar2uml/build.gradle @@ -38,7 +38,7 @@ try { version = oldProps['version'] } catch (e) { // this happens, if either the properties file is not present, or cannot be read from - throw new GradleException("File ${versionFile} not found or unreadable. Aborting.") + throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e) } task newVersion() { @@ -72,6 +72,10 @@ test { maxHeapSize = '1G' } +File preprocessorGrammar = file('../relast.preprocessor/src/main/jastadd/RelAst.relast') +File grammar2umlGrammar = file('./src/main/jastadd/Grammar2Uml.relast') +File intermediateGrammar = file('./src/main/jastadd/MustacheNodes.relast') + task relast(type: JavaExec) { group = 'Build' main = "-jar" @@ -86,9 +90,9 @@ task relast(type: JavaExec) { args = [ "../libs/relast.jar", - "../relast.preprocessor/src/main/jastadd/RelAst.relast", - "./src/main/jastadd/Grammar2Uml.relast", - "./src/main/jastadd/MustacheNodes.relast", + preprocessorGrammar, + grammar2umlGrammar, + intermediateGrammar, "--listClass=java.util.ArrayList", "--jastAddList=JastAddList", "--useJastAddNames", @@ -97,14 +101,14 @@ task relast(type: JavaExec) { "--grammarName=./src/gen/jastadd/Grammar2Uml" ] -// inputs.files file("../libs/relast.jar"), -// file("../relast.preprocessor/src/main/jastadd/RelAST.relast"), -// file("./src/main/jastadd/Grammar2Uml.relast") -// file("./src/main/jastadd/MustacheNodes.relast") -// outputs.files file("./src/gen/jastadd/Grammar2Uml.ast"), -// file("./src/gen/jastadd/Grammar2Uml.jadd"), -// file("./src/gen/jastadd/Grammar2UmlRefResolver.jadd"), -// file('./src/gen/jastadd/Grammar2UmlResolverStubs.jrag') + inputs.files(file("../libs/relast.jar"), + preprocessorGrammar, + grammar2umlGrammar, + intermediateGrammar) + outputs.files(file("./src/gen/jastadd/Grammar2Uml.ast"), + file("./src/gen/jastadd/Grammar2Uml.jadd"), + file("./src/gen/jastadd/Grammar2UmlRefResolver.jadd"), + file('./src/gen/jastadd/Grammar2UmlResolverStubs.jrag')) } jastadd { diff --git a/grammar2uml/src/main/jastadd/Navigation.jrag b/grammar2uml/src/main/jastadd/Navigation.jrag index 980904b34bb59d293da2512d5fea1d8375f457c0..ccd0f3d49ede4fbecfafafff05b5228e42c76ed9 100644 --- a/grammar2uml/src/main/jastadd/Navigation.jrag +++ b/grammar2uml/src/main/jastadd/Navigation.jrag @@ -16,5 +16,6 @@ aspect Navigation { // --- containedFileName --- eq Grammar2Uml.getChild().containedFileName() = getFileName(); eq Program.getChild().containedFileName() = null; + eq Grammar.getChild().containedFileName() = null; eq MGrammar2Uml.getChild().containedFileName() = null; } diff --git a/libs/plantuml.jar b/libs/plantuml.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3a204fc9cb7297951950e05c34f4bd270c5ebb6 Binary files /dev/null and b/libs/plantuml.jar differ