Skip to content

Commit

Permalink
IRProcessor generates Scala classes.
Browse files Browse the repository at this point in the history
This is the only way to retain 100% backward compatibility.
  • Loading branch information
Akirathan committed Oct 18, 2024
1 parent d2bb8c3 commit 68a070b
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ String getQualifiedTypeName() {
}

/**
* Returns true if this field extends {@link org.enso.compiler.core.ir.Expression}
* ({@link org.enso.compiler.core.ir.JExpression}).
* Returns true if this field extends {@link org.enso.compiler.core.ir.Expression} ({@link
* org.enso.compiler.core.ir.JExpression}).
*
* <p>This is useful, e.g., for the {@link org.enso.compiler.core.IR#mapExpressions(Function)}
* method.
*
* <p>This is useful, e.g., for the {@link org.enso.compiler.core.IR#mapExpressions(Function)} method.
* @return true if this field extends {@link org.enso.compiler.core.ir.Expression}
*/
boolean isExpression(ProcessingEnvironment processingEnv) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,23 @@ private void ensureIsSubtypeOfIR(TypeElement typeElem) {
}
}

/**
* Returns string representation of the class fields. Meant to be at the beginning of the class
* body.
*/
/** Returns string representation of the class fields. Meant to be in the default constructor. */
String fields() {
var userDefinedFields =
fields.stream()
.map(field -> "private final " + field.getSimpleTypeName() + " " + field.getName())
.collect(Collectors.joining(";" + System.lineSeparator()));
.map(field -> "private val " + field.getName() + ": " + field.getSimpleTypeName())
.collect(Collectors.joining("," + System.lineSeparator()))
+ ",";
if (fields.isEmpty()) {
userDefinedFields = "";
}
var code =
"""
$userDefinedFields;
// Not final on purpose
private DiagnosticStorage diagnostics;
private MetadataStorage passData;
private IdentifiedLocation location;
private UUID id;
$userDefinedFields
private var diagnostics: DiagnosticStorage = null,
private var passData: MetadataStorage = null,
private var location: IdentifiedLocation = null,
private var id: UUID = null
"""
.replace("$userDefinedFields", userDefinedFields);
return indent(code, 2);
Expand Down Expand Up @@ -170,7 +170,7 @@ String constructor() {
private String childrenMethodBody() {
var sb = new StringBuilder();
var nl = System.lineSeparator();
sb.append("var list = new ArrayList<IR>();").append(nl);
sb.append("val list = new ArrayList<IR>();").append(nl);
fields.stream()
.filter(Field::isChild)
.forEach(
Expand Down Expand Up @@ -204,61 +204,53 @@ String overrideIRMethods() {
var code =
"""
@Override
public MetadataStorage passData() {
override def passData(): MetadataStorage = {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public Option<IdentifiedLocation> location() {
override def location(): Option[IdentifiedLocation] {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public IR setLocation(Option<IdentifiedLocation> location) {
override def setLocation(location: Option[IdentifiedLocation]): IR = {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public IR mapExpressions(Function<Expression, Expression> fn) {
override def mapExpressions(fn: Function<Expression, Expression>): IR = {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public List<IR> children() {
override def children(): List<IR> = {
$childrenMethodBody
}
@Override
public @Identifier UUID getId() {
@Identifier
override def getId(): Identifier = {
if (id == null) {
id = UUID.randomUUID();
}
return id;
}
@Override
public DiagnosticStorage diagnostics() {
override def diagnostics(): DiagnosticStorage = {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public DiagnosticStorage getDiagnostics() {
override def getDiagnostics(): DiagnosticStorage = {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public IR duplicate(
override def duplicate(
boolean keepLocations,
boolean keepMetadata,
boolean keepDiagnostics,
boolean keepIdentifiers
) {
): IR = {
throw new UnsupportedOperationException("unimplemented");
}
@Override
public String showCode(int indent) {
override def showCode(indent: Int): String = {
throw new UnsupportedOperationException("unimplemented");
}
"""
Expand All @@ -278,9 +270,8 @@ String overrideUserDefinedMethods() {
.map(
field ->
"""
@Override
public $returnType $fieldName() {
return $fieldName;
override def $fieldName(): $returnType = {
return this.$fieldName;
}
"""
.replace("$returnType", field.getSimpleTypeName())
Expand All @@ -301,7 +292,7 @@ String builder() {
.map(
field ->
"""
private $fieldType $fieldName;
private var $fieldName: $fieldType = null,
"""
.replace("$fieldName", field.getName())
.replace("$fieldType", field.getSimpleTypeName()))
Expand All @@ -312,7 +303,7 @@ String builder() {
.map(
field ->
"""
public Builder $fieldName($fieldType $fieldName) {
def $fieldName($fieldName: $fieldType): Builder = {
this.$fieldName = $fieldName;
return this;
}
Expand All @@ -339,17 +330,18 @@ String builder() {

var code =
"""
public static final class Builder {
final class Builder(
$fieldDeclarations
) {
$fieldSetters
public $className build() {
def build(): $className = {
validate();
return new $className($fieldList);
}
private void validate() {
private def validate(): Unit = {
$validationCode
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaFileObject;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import org.enso.runtime.parser.dsl.IRChild;
import org.enso.runtime.parser.dsl.IRNode;

/** Generates Scala classes for every Java interface annotated with {@link IRNode}. */
@SupportedAnnotationTypes({
"org.enso.runtime.parser.dsl.IRNode",
"org.enso.runtime.parser.dsl.IRChild"
Expand Down Expand Up @@ -47,47 +49,44 @@ private void processIrNode(Element irNodeElem) {
var irNodeTypeElem = (TypeElement) irNodeElem;
var irNodeInterfaceName = irNodeTypeElem.getSimpleName().toString();
var pkgName = packageName(irNodeTypeElem);
var newClassName = irNodeInterfaceName + "Gen";
String newBinaryName;
if (!pkgName.isEmpty()) {
newBinaryName = pkgName + "." + newClassName;
} else {
newBinaryName = newClassName;
}
JavaFileObject srcGen = null;
var newCaseClassName = irNodeInterfaceName + "Gen";
FileObject srcGen = null;
try {
srcGen = processingEnv.getFiler().createSourceFile(newBinaryName, irNodeElem);
srcGen =
processingEnv
.getFiler()
.createResource(StandardLocation.SOURCE_OUTPUT, pkgName, newCaseClassName + ".scala");
} catch (IOException e) {
printError("Failed to create source file for IRNode", irNodeElem);
}
assert srcGen != null;
var irNodeElement = new IRNodeElement(processingEnv, irNodeTypeElem, newClassName);
var irNodeElement = new IRNodeElement(processingEnv, irNodeTypeElem, newCaseClassName);
try {
try (var lineWriter = new PrintWriter(srcGen.openWriter())) {
var code =
"""
$imports
public final class $className implements $interfaceName {
final class $className(
$fields
$constructor
public static Builder builder() {
return new Builder();
}
) extends $interfaceName {
$overrideUserDefinedMethods
$overrideIRMethods
}
final object $className {
def builder(): Builder = {
return new Builder();
}
$builder
}
"""
.replace("$imports", irNodeElement.imports())
.replace("$className", newClassName)
.replace("$className", newCaseClassName)
.replace("$fields", irNodeElement.fields())
.replace("$constructor", irNodeElement.constructor())
.replace("$interfaceName", irNodeInterfaceName)
.replace("$overrideUserDefinedMethods", irNodeElement.overrideUserDefinedMethods())
.replace("$overrideIRMethods", irNodeElement.overrideIRMethods())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ static boolean isSubtypeOfIR(TypeMirror type, ProcessingEnvironment processingEn
return processingEnv.getTypeUtils().isAssignable(type, irType);
}

/**
* Returns true if the given type extends {@link org.enso.compiler.core.ir.Expression}
*/
/** Returns true if the given type extends {@link org.enso.compiler.core.ir.Expression} */
static boolean isSubtypeOfExpression(TypeMirror type, ProcessingEnvironment processingEnv) {
var expressionType =
processingEnv.getElementUtils().getTypeElement("org.enso.compiler.core.ir.Expression").asType();
processingEnv
.getElementUtils()
.getTypeElement("org.enso.compiler.core.ir.Expression")
.asType();
return processingEnv.getTypeUtils().isAssignable(type, expressionType);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import com.google.testing.compile.CompilationSubject;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import org.enso.runtime.parser.processor.IRProcessor;
import org.junit.Test;

Expand Down Expand Up @@ -71,11 +72,11 @@ public interface JName extends IR {}
var compiler = Compiler.javac().withProcessors(new IRProcessor());
var compilation = compiler.compile(src);
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedSourceFile("JNameGen").isNotNull();
var genSrc = compilation.generatedSourceFile("JNameGen");
assertThat(genSrc.isPresent(), is(true));
var srcContent = readSrcFile(genSrc.get());
assertThat("Generated just one source", compilation.generatedSourceFiles().size(), is(1));
var generatedScalaClass =
compilation.generatedFile(StandardLocation.SOURCE_OUTPUT, "JNameGen.scala");
assertThat(generatedScalaClass.isPresent(), is(true));
var srcContent = readSrcFile(generatedScalaClass.get());
assertThat("Generated just one source", compilation.generatedFiles().size(), is(1));
}

@Test
Expand All @@ -97,14 +98,16 @@ public interface MyIR extends IR {
var compiler = Compiler.javac().withProcessors(new IRProcessor());
var compilation = compiler.compile(src);
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedSourceFile("MyIRGen").isNotNull();
var genSrc = compilation.generatedSourceFile("MyIRGen");
CompilationSubject.assertThat(compilation)
.generatedFile(StandardLocation.SOURCE_OUTPUT, "MyIRGen.scala")
.isNotNull();
var genSrc = compilation.generatedFile(StandardLocation.SOURCE_OUTPUT, "MyIRGen.scala");
assertThat(genSrc.isPresent(), is(true));
var srcContent = readSrcFile(genSrc.get());
assertThat("Generated just one source", compilation.generatedSourceFiles().size(), is(1));
}

private static String readSrcFile(JavaFileObject src) {
private static String readSrcFile(FileObject src) {
try {
return src.getCharContent(true).toString();
} catch (Exception e) {
Expand Down

0 comments on commit 68a070b

Please sign in to comment.