Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port TypeScript code generator to Kotlin. #431

Merged
merged 18 commits into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
512d568
Port preamble and parameter generation parts of TypeScript code gener…
hokeun Jul 25, 2021
6833440
Port reactor skeleton generation part of TypeScript code generator to…
hokeun Jul 25, 2021
f22b4e0
Port reaction generation part of TypeScript code generator to Kotlin.
hokeun Jul 25, 2021
e2cde0d
Port runtime part of TypeScript code generation to Kotlin.
hokeun Jul 25, 2021
661032b
Fix a few bugs in TypeScript reactor generation.
hokeun Jul 25, 2021
032400a
Kotlin code clean up.
hokeun Jul 25, 2021
acf6bd9
Add build and compile comments for Kotlin TypeScript generator.
hokeun Jul 25, 2021
89955f6
Switch TypeScript code generator from xtext to Kotlin in LFGenerator.…
hokeun Jul 25, 2021
7e35870
Fix compile error:
hokeun Jul 25, 2021
d8e5b5d
Fix error in code generation for reactor's typeParams for example, in…
hokeun Jul 26, 2021
04f8c9a
Fix errors in list and reference type code generation.
hokeun Jul 26, 2021
6afcb59
Fix errors in ProtoNoPacking.lf test, including proto file generation…
hokeun Jul 26, 2021
9f3114a
Address reviewers' comments:
hokeun Jul 26, 2021
7ea1942
Fix error in createFileConfig for targets other than CPP and TS.
hokeun Jul 26, 2021
bd6d6db
Let error messages read the enum directly. (toString() will be called…
hokeun Jul 26, 2021
2c50c49
Retrieve package name and class name prefix for the code generator in…
hokeun Jul 26, 2021
69778b4
Retrieve package name and class name prefix for the file config in Ko…
hokeun Jul 26, 2021
c77beff
Change class name prefixes for TypeScript target from Ts to TS.
hokeun Jul 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
129 changes: 71 additions & 58 deletions org.lflang/src/org/lflang/generator/LFGenerator.java
Expand Up @@ -47,55 +47,73 @@ private Target getTarget(Resource resource) {
return Target.forName(targetName);
}

/** Create a FileConfig object for the given target */
private FileConfig createFileConfig(final Target target, Resource resource,
IFileSystemAccess2 fsa, IGeneratorContext context)
throws IOException {
private String getPackageName(Target target) {
switch (target) {
case CPP: {
return createCppFileConfig(resource, fsa, context);
return "cpp";
}
case TS: {
return new TypeScriptFileConfig(resource, fsa, context);
return "ts";
}
default: {
return new FileConfig(resource, fsa, context);
throw new RuntimeException("Unexpected target!");
}
}
}

private String getClassNamePrefix(Target target) {
switch (target) {
case CPP: {
return "Cpp";
}
case TS: {
return "TS";
}
default: {
throw new RuntimeException("Unexpected target!");
}
}
}

/**
* Create a C++ specific FileConfig object
*
* Since the CppFileConfig class is implemented in Kotlin, the class is is
* Create a target-specific FileConfig object in Kotlin
*
* Since the CppFileConfig and TypeScriptFileConfig class are implemented in Kotlin, the classes are
* not visible from all contexts. If the RCA is run from within Eclipse via
* "Run as Eclipse Application", the Kotlin classes are unfortunately not
* available at runtime due to bugs in the Eclipse Kotlin plugin. (See
* https://stackoverflow.com/questions/68095816/is-ist-possible-to-build-mixed-kotlin-and-java-applications-with-a-recent-eclips)
*
* If the CppFileConfig class is found, this method returns an instance.
*
* If the FileConfig class is found, this method returns an instance.
* Otherwise, it returns an Instance of FileConfig.
*
* @return A CppFileConfig object if the class can be found
*
* @return A FileConfig object in Kotlin if the class can be found.
* @throws IOException
*/
private FileConfig createCppFileConfig(Resource resource,
IFileSystemAccess2 fsa, IGeneratorContext context)
throws IOException {
private FileConfig createFileConfig(final Target target,
Resource resource,
IFileSystemAccess2 fsa,
IGeneratorContext context)
throws IOException {
// Since our Eclipse Plugin uses code injection via guice, we need to
// play a few tricks here so that CppFileConfig does not appear as an
// play a few tricks here so that FileConfig does not appear as an
// import. Instead we look the class up at runtime and instantiate it if
// found.
if (target != Target.CPP && target != Target.TS) {
return new FileConfig(resource, fsa, context);
}
String packageName = getPackageName(target);
String classNamePrefix = getClassNamePrefix(target);
try {
return (FileConfig) Class
.forName("org.lflang.generator.cpp.CppFileConfig")
.getDeclaredConstructor(Resource.class,
IFileSystemAccess2.class, IGeneratorContext.class)
.newInstance(resource, fsa, context);
.forName("org.lflang.generator." + packageName + "." + classNamePrefix + "FileConfig")
.getDeclaredConstructor(Resource.class,
IFileSystemAccess2.class, IGeneratorContext.class)
.newInstance(resource, fsa, context);
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException
| ClassNotFoundException e) {
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException
| ClassNotFoundException e) {
return new FileConfig(resource, fsa, context);
}
}
Expand All @@ -110,60 +128,55 @@ private GeneratorBase createGenerator(Target target, FileConfig fileConfig,
case CCPP: {
return new CCppGenerator(fileConfig, errorReporter);
}
case CPP: {
return createCppGenerator(fileConfig, errorReporter);
}
case TS: {
return new TypeScriptGenerator(
(TypeScriptFileConfig) fileConfig, errorReporter);
}
case Python: {
return new PythonGenerator(fileConfig, errorReporter);
}
default: {
throw new RuntimeException("Unexpected target!");
return createKotlinGenerator(target, fileConfig, errorReporter);
}
}
}

/**
* Create a C++ code generator
*
* Since the CppGenerator class is implemented in Kotlin, the class is
* Create a code generator in Kotlin.
*
* Since the CppGenerator and TSGenerator class are implemented in Kotlin, the classes are
* not visible from all contexts. If the RCA is run from within Eclipse via
* "Run as Eclipse Application", the Kotlin classes are unfortunately not
* available at runtime due to bugs in the Eclipse Kotlin plugin. (See
* https://stackoverflow.com/questions/68095816/is-ist-possible-to-build-mixed-kotlin-and-java-applications-with-a-recent-eclips)
* In this case, the method returns null
*
* @return A CppGenerator object if the class can be found
*
* @return A Kotlin Generator object if the class can be found
*/
private GeneratorBase createCppGenerator(FileConfig fileConfig,
ErrorReporter errorReporter) {
private GeneratorBase createKotlinGenerator(Target target, FileConfig fileConfig,
ErrorReporter errorReporter) {
// Since our Eclipse Plugin uses code injection via guice, we need to
// play a few tricks here so that CppFileConfig and CppGenerator do not
// appear as an import. Instead we look the class up at runtime and
// instantiate it if found.
// play a few tricks here so that Kotlin FileConfig and
// Kotlin Generator do not appear as an import. Instead we look the
// class up at runtime and instantiate it if found.
String packageName = getPackageName(target);
String classNamePrefix = getClassNamePrefix(target);
try {
return (GeneratorBase) Class
.forName("org.lflang.generator.cpp.CppGenerator")
.getDeclaredConstructor(
Class.forName(
"org.lflang.generator.cpp.CppFileConfig"),
ErrorReporter.class, LFGlobalScopeProvider.class)
.newInstance(fileConfig, errorReporter, scopeProvider);
.forName("org.lflang.generator." + packageName + "." + classNamePrefix + "Generator")
.getDeclaredConstructor(
Class.forName(
"org.lflang.generator." + packageName + "." + classNamePrefix + "FileConfig"),
ErrorReporter.class, LFGlobalScopeProvider.class)
.newInstance(fileConfig, errorReporter, scopeProvider);
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException
| ClassNotFoundException e) {
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException
| ClassNotFoundException e) {
generatorErrorsOccurred = true;
errorReporter.reportError(
"The code generator for the C++ target could not be found. "
+ "This is likely because you are running the RCA from Eclipse. "
+ "The C++ code generator is written in Kotlin and, "
+ "unfortunately, the Eclipse Kotlin plugin is broken, "
+ "preventing us from loading the generator properly. "
+ "Please consider building the RCA via Maven.");
"The code generator for the " + target + " target could not be found. "
+ "This is likely because you are running the RCA from"
+ "Eclipse. The " + target + " code generator is written in Kotlin"
+ "and, unfortunately, the Eclipse Kotlin plugin is "
+ "broken, preventing us from loading the generator"
+ "properly. Please consider building the RCA via Maven.");
// FIXME: Add a link to the wiki with more information.
return null;
}
Expand Down
66 changes: 66 additions & 0 deletions org.lflang/src/org/lflang/generator/ts/TSFileConfig.kt
@@ -0,0 +1,66 @@
/*************
* Copyright (c) 2019-2020, The University of California at Berkeley.

* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:

* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.

* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***************/

package org.lflang.generator.ts

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import org.lflang.FileConfig
import java.io.IOException
import java.nio.file.Path

/**
* Generator for TypeScript target.
*
* @author{Matt Weber <matt.weber@berkeley.edu>}
* @author{Edward A. Lee <eal@berkeley.edu>}
* @author{Marten Lohstroh <marten@berkeley.edu>}
* @author {Christian Menard <christian.menard@tu-dresden.de>}
* @author {Hokeun Kim <hokeunkim@berkeley.edu>}
*/
class TSFileConfig(resource: Resource, fsa: IFileSystemAccess2,
context: IGeneratorContext) :
FileConfig(resource, fsa, context) {

/**
* Clean any artifacts produced by the TypeScript code generator.
*/
@Throws(IOException::class)
override fun doClean() {
super.doClean()
deleteDirectory(getSrcGenPath())
}

/**
* Path to TypeScript source code.
*/
fun tsSrcGenPath(): Path = getSrcGenPath().resolve("src")

/**
* Path to TypeScript core source code.
*/
fun tsCoreGenPath(): Path = tsSrcGenPath().resolve("core")
}