From 8be383305b8cd768a34890b0e0e27b2663fc13b7 Mon Sep 17 00:00:00 2001 From: homedirectory Date: Wed, 7 Feb 2024 19:52:44 +0200 Subject: [PATCH 1/2] Ensure that keywords aren't used for variable names This corrects the generation of variable names corresponding to terminal parameters, such as Class or Enum. Previous implementation could erronously generate code such as: `java.lang.Enum enum = ...;` or `Type method(Class class)` --- .../grammar/types/ClassParameter.java | 20 ++++++------------- .../technion/cs/fling/namers/NaiveNamer.java | 8 ++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/il/ac/technion/cs/fling/internal/grammar/types/ClassParameter.java b/src/main/java/il/ac/technion/cs/fling/internal/grammar/types/ClassParameter.java index 409f3010..f4fa42da 100644 --- a/src/main/java/il/ac/technion/cs/fling/internal/grammar/types/ClassParameter.java +++ b/src/main/java/il/ac/technion/cs/fling/internal/grammar/types/ClassParameter.java @@ -1,6 +1,8 @@ package il.ac.technion.cs.fling.internal.grammar.types; + +import static il.ac.technion.cs.fling.namers.NaiveNamer.lowerCamelCase; +import static il.ac.technion.cs.fling.namers.NaiveNamer.unreservedName; import static java.util.Objects.requireNonNull; -import il.ac.technion.cs.fling.namers.NaiveNamer; // TODO allow primitive types. public class ClassParameter implements StringTypeParameter { public final Class parameterClass; @@ -11,7 +13,9 @@ public ClassParameter(final Class parameterClass) { return parameterClass.getCanonicalName(); } @Override public String baseParameterName() { - return unPrimitiveTypeSimple(NaiveNamer.lowerCamelCase(parameterClass.getSimpleName())); + if (parameterClass.isPrimitive()) + return parameterClass.getSimpleName().substring(0, 1); + return unreservedName(lowerCamelCase(parameterClass.getSimpleName())); } @Override public int hashCode() { return parameterClass.hashCode(); @@ -39,16 +43,4 @@ public static String unPrimitiveType(final String typeName) { void.class.getName().equals(typeName) ? Void.class.getCanonicalName() : // typeName; } - public static String unPrimitiveTypeSimple(final String typeName) { - return byte.class.getName().equals(typeName) ? "b" : // - short.class.getName().equals(typeName) ? "s" : // - int.class.getName().equals(typeName) ? "i" : // - long.class.getName().equals(typeName) ? "l" : // - float.class.getName().equals(typeName) ? "f" : // - double.class.getName().equals(typeName) ? "d" : // - boolean.class.getName().equals(typeName) ? "b" : // - char.class.getName().equals(typeName) ? "c" : // - void.class.getName().equals(typeName) ? "v" : // - typeName; - } } diff --git a/src/main/java/il/ac/technion/cs/fling/namers/NaiveNamer.java b/src/main/java/il/ac/technion/cs/fling/namers/NaiveNamer.java index a85b3b76..e33e438c 100644 --- a/src/main/java/il/ac/technion/cs/fling/namers/NaiveNamer.java +++ b/src/main/java/il/ac/technion/cs/fling/namers/NaiveNamer.java @@ -23,6 +23,9 @@ import il.ac.technion.cs.fling.internal.grammar.rules.Component; import il.ac.technion.cs.fling.internal.grammar.rules.Constants; import il.ac.technion.cs.fling.internal.grammar.rules.Variable; + +import javax.lang.model.SourceVersion; + public class NaiveNamer implements Namer { private final Map astChildrenCounter = new HashMap<>(); private final Map notationsChildrenCounter = new HashMap<>(); @@ -168,6 +171,11 @@ public static String getNameFromBase(final String baseName, final Map Date: Thu, 8 Feb 2024 16:39:30 +0200 Subject: [PATCH 2/2] Refactor the AST visitor to be useful AST visitor generation was producing visitors that were hardly useful: 1. `visit` methods were final which prevented implementations from controlling the descending behaviour 2. `whileVisiting` methods were hardly useful due to point 1 Consider the task of wrapping a parent AST node in parentheses, including its children. This could not be achieved using previous AST visitors. This change makes visitors be generated in the fashion of javax.lang.model.util.ElementScanner. It allows implementations to control the descent like so: void visit(ParentNode node) { print("("); // before visiting children Visitor.super.visit(node); // descend print(")"); // after having visited children } --- .../fling/adapters/JavaASTVisitorAdapter.java | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/main/java/il/ac/technion/cs/fling/adapters/JavaASTVisitorAdapter.java b/src/main/java/il/ac/technion/cs/fling/adapters/JavaASTVisitorAdapter.java index 075619f9..f25c6394 100644 --- a/src/main/java/il/ac/technion/cs/fling/adapters/JavaASTVisitorAdapter.java +++ b/src/main/java/il/ac/technion/cs/fling/adapters/JavaASTVisitorAdapter.java @@ -1,117 +1,117 @@ package il.ac.technion.cs.fling.adapters; -import static java.util.stream.Collectors.joining; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; + import il.ac.technion.cs.fling.internal.compiler.Namer; -import il.ac.technion.cs.fling.internal.compiler.ast.nodes.ASTCompilationUnitNode; -import il.ac.technion.cs.fling.internal.compiler.ast.nodes.AbstractClassNode; -import il.ac.technion.cs.fling.internal.compiler.ast.nodes.ClassNode; -import il.ac.technion.cs.fling.internal.compiler.ast.nodes.ConcreteClassNode; -import il.ac.technion.cs.fling.internal.compiler.ast.nodes.FieldNode; +import il.ac.technion.cs.fling.internal.compiler.ast.nodes.*; import il.ac.technion.cs.fling.internal.grammar.rules.Variable; import il.ac.technion.cs.fling.internal.grammar.sententials.quantifiers.JavaCompatibleQuantifier; import il.ac.technion.cs.fling.namers.NaiveNamer; -/** Java adapter printing AST visitor class given AST type definitions. + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static java.lang.String.format; +import static java.util.stream.Collectors.joining; + +/** + * Java adapter printing AST visitor class given AST type definitions. * - * @author Ori Roth */ -@SuppressWarnings("static-method") public class JavaASTVisitorAdapter { + * @author Ori Roth + */ +@SuppressWarnings("static-method") +public class JavaASTVisitorAdapter { private final String astClassName; private final Namer namer; private final String packageName; + public JavaASTVisitorAdapter(final String packageName, final String astClassName, final Namer namer) { this.packageName = packageName; this.astClassName = astClassName; this.namer = namer; } + public String printASTVisitorClass(final ASTCompilationUnitNode compilationUnit) { - return String.format("public static class %s{%s%s}", // - VISITOR_CLASS_NAME, // - compilationUnit.classes.stream() // - .map(this::printVisitMethod) // - .collect(joining()), // - compilationUnit.classes.stream() // - .filter(ClassNode::isConcrete) // - .map(ClassNode::asConcrete) // - .map(this::printWhileVisitingMethod) // - .collect(joining())); + return format("public interface %s{%s}", // + VISITOR_CLASS_NAME, // + compilationUnit.classes.stream() // + .map(this::printVisitMethod) // + .collect(joining())); } + public String printVisitMethod(final AbstractClassNode clazz) { final Variable source = clazz.source; final String parameterName = getNodeParameterName(source); - return String.format("public final void visit(%s %s){%s}", // - getASTVariableClassName(source), // - parameterName, // - printVisitMethodBody(clazz, parameterName)); + return format("default void visit(%s %s){%s}", // + getASTVariableClassName(source), // + parameterName, // + printVisitMethodBody(clazz, parameterName)); } + public String printVisitMethod(final ClassNode clazz) { return clazz.isAbstract() ? // - printVisitMethod(clazz.asAbstract()) : // - printVisitMethod(clazz.asConcrete()); + printVisitMethod(clazz.asAbstract()) : // + printVisitMethod(clazz.asConcrete()); } + public String printVisitMethod(final ConcreteClassNode clazz) { final Variable source = clazz.source; final String parameterName = getNodeParameterName(source); - return String.format("public final void visit(%s %s){%s}", // - getASTVariableClassName(source), // - parameterName, // - printVisitMethodBody(clazz, parameterName)); - } - public String printWhileVisitingMethod(final ConcreteClassNode clazz) { - final Variable source = clazz.source; - final String parameterName = getNodeParameterName(source); - return String.format("public void whileVisiting(%s %s)throws %s{}", // - getASTVariableClassName(source), // - parameterName, // - Exception.class.getCanonicalName()); + return format("default void visit(%s %s){%s}", // + getASTVariableClassName(source), // + parameterName, // + printVisitMethodBody(clazz, parameterName)); } + private String getASTVariableClassName(final Variable variable) { - return String.format("%s.%s.%s", // - packageName, // - astClassName, // - namer.getASTClassName(variable)); + return format("%s.%s.%s", // + packageName, // + astClassName, // + namer.getASTClassName(variable)); } + private String getNodeParameterName(final Variable variable) { return NaiveNamer.lowerCamelCase(variable.name()); } + private String printVisitMethodBody(final AbstractClassNode clazz, final String parameterName) { return clazz.children.stream() // - .map(child -> String.format("if(%s instanceof %s)%s", // - parameterName, // - getASTVariableClassName(child.source), // - variableVisitingStatement(child.source, parameterName))) // - .collect(joining("else ")); + .map(child -> format("if(%s instanceof %s)%s", // + parameterName, // + getASTVariableClassName(child.source), // + variableVisitingStatement(child.source, parameterName))) // + .collect(joining("else ")); } + private String printVisitMethodBody(final ConcreteClassNode clazz, final String parameterName) { final StringBuilder $ = new StringBuilder(); final Map usedNames = new LinkedHashMap<>(); - $.append(String.format("try{this.whileVisiting(%s);}catch(%s __){__.printStackTrace();}", // - parameterName, // - Exception.class.getCanonicalName())); clazz.fields.stream() // - .map(FieldNode::source) // - .forEach(source -> { - assert !source.isQuantifier() || source.getClass() - .isAnnotationPresent(JavaCompatibleQuantifier.class) : "BNF uses a non-Java-compatible notation"; - }); + .map(FieldNode::source) // + .forEach(source -> { + assert !source.isQuantifier() || source.getClass() + .isAnnotationPresent(JavaCompatibleQuantifier.class) : "BNF uses a non-Java-compatible notation"; + }); clazz.fields.stream() // - .map(FieldNode::getInferredFieldFragments) // - .flatMap(List::stream) // - .map(field -> field.visitingStatement(// - this::variableVisitingStatement, // - String.format("%s.%s", // - parameterName, // - field.parameterName), // - () -> NaiveNamer.getNameFromBase("_x_", usedNames))) - .filter(Objects::nonNull) // - .forEach($::append); + .map(FieldNode::getInferredFieldFragments) // + .flatMap(List::stream) // + .map(field -> field.visitingStatement(// + this::variableVisitingStatement, // + format("%s.%s", // + parameterName, // + field.parameterName), // + () -> NaiveNamer.getNameFromBase("_x_", usedNames))) + .filter(Objects::nonNull) // + .forEach($::append); return $.toString(); } + private String variableVisitingStatement(final Variable variable, final String access) { - return String.format("{visit((%s)%s);}", // - getASTVariableClassName(variable), // - access); + return format("{visit((%s)%s);}", // + getASTVariableClassName(variable), // + access); } + private static final String VISITOR_CLASS_NAME = "Visitor"; + }