Giter VIP home page Giter VIP logo

gryphon's Introduction

⚠️ Repository archived

This project has been archived and is no longer maintained. The original maintainer is no longer able to dedicate the time required to keep Gryphon up to date with the recent Swift releases. The code is preserved here for any who may want it, and the original Readme.md follows below:


Gryphon logo

The Swift to Kotlin translator

Swift package manager compatible open ethical licensed ethically Gitpod Ready-to-Code first-timers-only Follow on Twitter

Gryphon is a program that translates Swift code into Kotlin code. It was created to enable mobile app developers to share parts of an iOS app's codebase with Android.

  • Risk-free. Stop using Gryphon whenever you want - you'll still be able to read and understand your Kotlin code, even the computer-generated parts.
  • No editing needed. Translated Kotlin files work the same as the Swift files they came from.
  • Xcode integration. Translate your iOS code to Android, compile the Android app, and see Kotlin's errors and warnings in the Swift lines that originated them - all without leaving Xcode.
  • Custom-made. Use special comments and templates to customize your Kotlin translation, and use any platform-specific features you want - even in translated source files.

👍 Status

Gryphon is now in preview! 🎉

This means the main systems and ideas have already been implemented - for instance, it's been translating a complete version of its own codebase for a while now. However, users often find new bugs to be fixed. If that's the case, feel free to report a new issue on GitHub.

You can also check out the currently supported translations for the Swift standard library here.

📲 Installing

Gryphon supports both macOS and Linux. You can install it with:

Homebrew

Use Homebrew to install Gryphon and its dependencies automatically:

$ brew install vinivendra/gryphon/gryphon

Mint

Use Mint to install Gryphon using the Swift package manager:

$ mint install vinivendra/Gryphon

GitPod

Try it out on GitPod before downloading:

Open in Gitpod

Building from source

Clone the repo run the installation script:

$ git clone https://github.com/vinivendra/Gryphon.git
$ cd Gryphon
$ ./Scripts/install.sh

Docker

Install it in a Docker container:

$ git clone https://github.com/vinivendra/Gryphon.git
$ cd Gryphon
$ docker build -t gryphon .
$ docker run -it --rm --privileged -v /absolute/path/to/current/directory/:/app/Gryphon gryphon
# ./Scripts/install.sh

📖 Guides

Check out the Tutorial to get started. It covers the basic information needed to begin using Gryphon, whether you want to translate command line programs, translate a new iOS app to Android, or add Gryphon to an existing app.

There are also more advanced guides on using collections, using translation comments, and using templates

📘 Frequently asked questions

What is Gryphon?

Gryphon is a command line application that can translate Swift code into Kotlin code. It was especially designed to enable app developers to translate platform-independent parts of their iOS apps into code they can use in their Android apps.

Can I compile and run the translated code or do I need to fix it first?

Gryphon's output code is meant to behave just like the input code that created it. It's still possible technically to generate Kotlin code that doesn't compile - for instance, if you try to translate unsupported Swift features, or if there's a bug - but as a rule, you should be able to translate, compile and run supported code without the need for post-translation edits.

Will I be able to understand the translated code?

One of Gryphon's main goals is to make sure translated code can be understood by human beings. As a rule, if you understand the input Swift code, you should also be able to understand the translated Kotlin code - if you don't, feel free to file a bug report.

This is done within some realistic constraints: the priority is that the translated code has to behave correctly, for instance. Gryphon attempts to find a "reasonably understandable Kotlin" middle ground somewhere between "machine-code-like Kotlin" and "perfectly idiomatic Kotlin".

Can I translate anything written in Swift?

Gryphon's support for Swift features is constantly evolving. It is currently capable of translating many of the main features one might expect - classes, structs, enums, closures, extensions, protocols, etc - enough that it currently translates around 97% of a version of its own codebase (the other 3% are platform-specific files). Some Swift features are just waiting to be implemented, while others can't be translated to Kotlin and may never be supported.

What Swift versions can Gryphon translate?

Gryphon currently supports Swift 5.2 to 5.5. It will use whatever Swift version was used to build it, so if you build the Gryphon binary using Swift 5.5 then that's the version it will assume all your Swift code is in. You can find out which Swift version your Gryphon binary uses by running gryphon --version.

Due to some technical limitations, only Swift 5.3, 5.4 and 5.5 are being tested in the latest versions of Gryphon. If you have any difficulties with Swift 5.2, please let us know.

Can Gryphon help translate my existing iOS app?

Yes - but it will need some adaptations (though probably less than your average multiplatform framework). This depends on your application - how similar app's architecture is to its Android counterpart, how often your code uses Swift features unsupported by Gryphon, etc.

It's worth noting that, like other transpilers for app development, Gryphon is best suited for translating platform-independent logic code. There's currently no support for translating calls to UIKit, for instance - and there's no telling if that will happen someday.

It is recommended that you start by translating only a few platform-independent parts of your code, adding new files incrementally. It might help to use architectures with clear separations between UI code and logic code - like MVP and MVC to separate the code that can be translated. For more information, check out Adding Gryphon to an existing app.

Can I use Gryphon to translate a non-iOS app?

Yes. While Gryphon's main focus is on iOS-to-Android support, it is primarily a Swift to Kotlin translator, and it doesn't require anything iOS-specific to run. You can use it on Linux to translate command line tools, for example. Even Gryphon's own source code can be translated, and that's just a command-line tool with nothing iOS-related.

Will it ever support translating Kotlin code to Swift? What about other languages?

Probably not. The challenges involved in translating Swift code into Kotlin are very specific for these two languages. Translating Kotlin into Swift would require a new front-end for Kotlin, a new back-end for Swift, and all-new logic in the middle to turn one into the other - basically, a whole new Gryphon. The same goes for other combinations of languages.

How can I help?

Thanks for the offer!

If you want to suggest a way to improve Gryphon, feel free to open a new issue. All bug reports and feature requests are welcome and encouraged - we can't fix it if we don't know about it!

If you would like to contribute directly, first check out the contributor's guide to learn to set up your environment. Then, if you're looking for inspiration, take a look at some good first issues (if you're new to Gryphon) or the beginner-friendly first timers only (if you're new to open source) - or, if you already know what you want to do, open an issue and let's talk about it.

If you're going to contribute, you should probably read the code of conduct too.

gryphon's People

Contributors

barrault01 avatar codersanjeev avatar igormaineti avatar pt2121 avatar sgade avatar troy-lamerton avatar vinivendra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gryphon's Issues

Refactor the type system

There are several complicated algorithms (many of which haven't been implemented yet, like #63) that would be easier to do if we had a decent type system.

Instead of using strings for all types, we could have a more complicated structure. Something like

indirect enum SwiftType {
    case simpleType(String) // `Int`
    case functionType(parameter: [String], return: String) // `(Int) -> Int
    case tupleType([String]) // (Int, Int)
    case dotType(left: String, right: String) // String.Index
    case genericType(type: String, genericParameters: [String], constraints: [TypeConstraint]) // Array<Element> where Element :Equatable
}

Support automatic override

Is your feature request related to a problem? Please describe.
It is just not optimal to use the gryphon annotation for override, also I always forget to add it and then I discover the problem when trying to use it.

Describe the solution you'd like
It would be really nice if a interface implementation already came with the override annotation

So for this Swift code:

protocol A {
    var b: Double { get set }
    
    func f() -> String
}

class Foo : A {
    
    var b: Double
    
    init(b: Double) {
        self.b = b
    }
    
    func f() -> String {
        
    }
}

I would like to get this Kotlin code

internal interface A {
    var b: Double

    fun f(): String
}

internal open class Foo: A {
    override var b: Double

    constructor(b: Double) {
        this.b = b
    }

    override fun f(): String {
    }
}

Describe alternatives you've considered
Since it is hard to know if a function is regarding the implementation of the interface, if the function signature or the variable matches the implemented interface, add the override annotation

Support generic classes

Generic information for classes is currently ignored. Make sure the class's header is translated correctly, as well as its members. Also, add tests.

Array's firstIndex(of:) needs a better translation

The array.firstIndex(of:) method fails by returning nil on Swift and -1 on Kotlin, which can make it hard to write translatable code using it.

Actions needed:

  • Either make up a better translation or add a new method to the GryphonKotlinLibrary.
  • Check if this method is used in the Gryphon codebase and update the uses accordingly.

Support number type conversion

Describe the bug
When converting numbers the outcome in wrong.

If you init a double in swift:
Double(intVariable)

It should output this in kotlin:
intVariable.toDouble()

Instead it is outputing:
Double(intVariable)

This behavior should be consistent from and to every number type.

Your environment (please complete the following information):

  • OS: macOS
  • Gryphon version: 0.7

Remove break statements in switches

Switches in Swift can have break statements in their cases. This is often used for cases that have no other statements, but it can in used in cases with multiple statements too:

switch number {
case 0:
	print("Hello!")
case 1:
	break
case 2:
	break
	print("Hello!")
default:
	print("Hello!")
	break
}

In the example above, Gryphon would currently include the break statements used on case 2 and on default. This is a bug, since the Kotlin compiler would throw errors for these breaks.

A correct translation would probably remove all break statements from switch cases. It should take care to raise a warning when there are statements after the break (as in case 2), otherwise the translated code would be incorrect (the Kotlin code would print "Hello" in case 2 but the Swift code wouldn't).

Automatically escape dollar signs

Dollar signs ($'s) in Swift strings may cause problems when translated directly, since Kotlin uses them as an escape character. It may be possible to always escape them during the translation, which should fix this problem.

If this gets done, it might be possible to simplify the handling of dollar signs in AuxiliaryFileContents.swift, and maybe other files too.

Translate Dictionary's mapValues

Add a library translation for Dictionary.mapValues.

This can be difficult because Swift's Dictionary.mapValues takes a closure that receives a tuple of type (Key, Value) as a parameter. Kotlin's mapValues, however, takes a closure that receives two parameters, one of type Key and one of type Value.

Wrong return call inside lambda with guard

Describe the bug
returning from inside a guard after doing something inside the else outputs wrong kotlin code

How to reproduce it
Given the swift code:

protocol Test {
    var a: Int { get set }
}

func foo(arg: @escaping (Test?) -> ()) {
    
}

func baz() {
    var k = 0
    foo(arg: { t in
        guard let td = t else {
            k = 1
            return
        }
    })
}

It outputs

internal interface Test {
    var a: Int
}

internal fun foo(arg: (Test?) -> Unit) {
}

internal fun baz() {
    var k: Int = 0
    foo { t ->
            val td: Test? = t

            if (td == null) {
                k = 1
                return@foo(arg:)
            }
        }
}

The correct thing to do inside the lambda return is return@foo

Your environment (please complete the following information):

  • OS: macOS
  • Gryphon version: version 0.8

Argument label before the parameter name in Swift are not ignored correctly

This input in Swift:

public class A {
    var foo: String
    init(with a: String) {
        foo = a
    }
}
A(with: "foo")

Should return:

open class A {
    internal open var foo: String
    constructor(a: String) {
        foo = a
    }
}
fun main(args: Array<String>) {
    A(a = "asdf")
}

Today it returned:

open class A {
    internal open var foo: String
    constructor(a: String) {
        foo = a
    }
}
fun main(args: Array<String>) {
    A(with = "asdf")
}

Raise warning for Xcode file without .kt output

When translating an Xcode project it seems reasonable to assume the user doesn't want the output to be printed to the console. We could raise a warning when we find an input file (in the xcfilelist) that doesn't have an output comment so that the user realizes the problem quickly.

Float(string) and similar functions need a better translation

The current translation of Float(string) turns it into string.toFloat(). This can be a problem when the translations fail: the Swift version returns nil and the Kotlin version throws an error.

This problem seems to be similar to that in #2.

A way to solve both these problems (and possibly more to come) is to add something like this to the GryphonKotlinLibrary:

fun <T> tryOrNull(autoclosure: () -> T): T? {
	try {
		return autoclosure()
	}
	catch (exception: Exception) {
		return null
	}
}

fun <T> T.nullIf(value: T): T? {
	if (this == value) {
		return null
	}
	else {
		return this
	}
}

fun main(args: Array<String>) {
	val a: Float? = tryOrNull { "abc".toFloat() } // null
	val b: Float? = tryOrNull { "0".toFloat() } // 0.0f

	val c: Int? = (-1).nullIf(-1) // null
	val d: Int? = (3).nullIf(-1) // 3
}

It might seem unintuitive to users at first, but it might be better than creating several individual functions to fix each of these problems.

Closure translation not working properly when define like this: () -> Void

Let’s say you have a closure declares like this:
(1) var completion: (() -> Void)

You can also declare it like this:

(2) var completion: (() -> ())

Both approaches are correct and should be translated equally.

Like this:

var completion: () -> Unit

Current result with the lib:
(2) is correct
(1) produce this:
var completion: () -> Void
and this not complied in Kotlin

Support autoclosures with tuple shuffle expressions

Currently, autoclosures only get translated correctly when the function call is a TupleExpression, but not when it's a TupleShuffleExpression. For instance, this is translated correctly:

func f(b: @autoclosure () -> Int) { }
f(b: 10)

But this isn't:

func f(a: Int = 0, b: @autoclosure () -> Int = 0) { }
f(b: 10)

Improve verbose logging

Is your feature request related to a problem? Please describe.
It can be hard to help users diagnose their problems without some idea of what the program did (or was doing) when the problem happened. Better progress logging could make this easier, even if hidden behind the --verbose flag.

Describe the solution you'd like
The improved logging should say what Gryphon is doing at any given time, so that if it crashes we know what it did before and what it was doing then. It should have a decent level of granularity, enough to be useful but not to much so it isn't confusing.

Describe alternatives you've considered
None.

Additional context
None.

Imitate original vertical whitespace

Vertical whitespace is currently added in an arbitrary way, but the ranges in the AST nodes may allow us to add it in a way that imitates the input code, probably leading to more readable outputs.

Support generic structs

Generic information for structs is currently ignored. Make sure the struct's header is translated correctly, as well as its members. Also, add tests.

This should be easier once issue #10 is done, or vice-versa.

Add automatic `package` statement to the Kotlin library

When the Kotlin library is generated and added to an Android app, it doesn't come with a package statement, which must be added manually by the user. Not adding this statement causes the Android app not to include the library in the compilation, a bug that might be difficult to diagnose. Adding a (purposefully fake) statement like package com.example.myAppName might make Android Studio raise an error in that line, which would make the bug trivial to solve.

Alternatively, we could try to get the Android app's name, maybe by inspecting the ANDROID_ROOT variable in the Kotlin target or by inspecting some gryphon output comments, but that seems hacky and it might lead to problems.

Add tests for insert comments in multiline strings

There should be a test that ensures insert comments on multiline strings are ignored. This means the following code

// gryphon multiline
let sourceFileContents = """
	let x: Int = 0 // gryphon ignore
	// gryphon insert: let x: Int = 0

	"""

should be translated to something like

val sourceFileContents: String = """
	let x: Int = 0 // gryphon ignore
	// gryphon insert: let x: Int = 0

	"""

that is, no statements should be ignored or inserted because of the "ignore" and "insert" comments inside the string. This new test can probably be added to SourceFileTest.testGetTranslationCommentFromLine. It should probably wait for issue #4 to be solved.

Improve templates for the Swift standard library

The templates file currently covers a few parts of the Swift standard library, mainly Strings and Arrays. This issue will be used to keep track of expanding the standard library support to other types and methods.

Translate dictionary's sort

Kotlin's Maps don't implement a sorting mechanism that's compatible with Swift, just like the Lists didn't. In order to translate a Dictionary's sort, we'll probably have to implement a quicksort operation or something, just like we did with List.

Once this is decided and either implemented or discarded, we should also either translate or delete the (Mutable)MapTest.testSortedBy tests.

List casts crash on Swift but throw errors on Kotlin

This could cause problems if a method on the stack catches errors, since it could cause Kotlin code to behave differently. The best solution would probably be to crash on Kotlin too, maybe using something like what's used for fatalError translations.

Translate access modifiers for extensions

Currently (as of Swift 5.1.3) extensions don't include their access modifiers when their AST is dumped. This is an issue with the Swift compiler that stops Gryphon being able to translate access modifiers in extensions.

Fixing this issue would allow for extensions to be treated as basically any other declaration in the access modifier algorithm.

Ignore `// gryphon multiline` comments

All // gryphon multiline comments should be ignored during translation and should not appear in the translated Kotlin code.

Example:

// gryphon multiline
let a = """

abcde

"""

...gets translated to...

// gryphon multiline
internal val a: String = """
abcde
"""

The // gryphon multiline comment at the start of the Kotlin translation should not be there.

Refactor tupleExpression and tupleShuffleExpression

These two classes share a lot of the same code and functions. We might still have to differentiate between them for a few things (i.e. trailing closures work differently for tuple shuffles) but it might be possible to make on a subclass of the other, or to make them the same class with a differentiating boolean, or something.

Support Swift complex guard

Describe the bug
When using a chained guard as below

struct Foo {
    var baz: Double?
}

func foo(param: Foo?) -> Double? {
    guard let p = param,
        let result = p.baz else {
            return nil
    }
    return result
}

The result is

internal data class Foo(
    var baz: Double? = null
)

internal fun foo(param: Foo?): Double? {
    val p: Foo? = param
    val result: Double? = p.baz

    if (!(p != null && result != null)) {
        return null
    }
    return result
}

But when calling p.baz p is optional, so it should insert the optional operator like this p?.baz

Your environment (please complete the following information):

  • OS: macOS
  • Gryphon version: 0.7

Add some structure to template strings

Templates currently are translated as literal strings, with some replacements for relevant expressions. For instance, array.first gets translated to the literal string "_array.first", replacing "_array" for the matched expression.

This system is limited because there is no information about the structure of the literal string. In this example, we don't know the string contains a dot expression. This means, for instance, we don't know we might have to add optionals to it in a dot expression chain, which can result in incorrect code.

It would be useful to have some way of transmitting this kind of information, so that we could translate templates as more complex ASTs. One option would be to have users write out an AST of sorts that we would then convert to a GryphonAST:

func gryphonTemplates() {
    let _array: [Any?] = []

    _array.first
    GRYDotExpression(
        GRYTemplateExpression("_array"),
        GRYCallExpression("firstOrNull", []))
}

This could be more complex than the example above (i.e. if it mirrored the GryphonAST exactly), but it could also be simpler:

func gryphonTemplates() {
    let _array: [Any?] = []

    _array.first
    GRYDotExpression("_array", "firstOrNull()")
}

This system has the downside of requiring users to import this API into their project.

We could alternatively create rules for interpolating strings/adding strings, which can be more friendly but runs the risk of false positives (and of forcing users to write long strings):

func gryphonTemplates() {
    let _array: [Any?] = []

    _array.first
    "_array" + "." + "firstOrNull" + "()"
}

Support for optional function type invoke

Describe the bug
Invoking an optional function type is not being properly translated.

How to reproduce it
When invoking a optional function type in Swift
foo?()

The result in Kotlin is
foo?()

When it should be
foo?.invoke()

Your environment (please complete the following information):

  • OS: macOS
  • Gryphon version: 0.7

Support multiple defers in a single block

Gryphon currently flatMaps all defer statements it finds in a function's body into a single try/finally block. This can cause correctness issues. A better way to translate multiple defer statements would something like:

  • For every statement in a block:
    • If it's a normal statement, add it to the block.
    • If it's a defer statement:
      • Add all subsequent statements to a new try block, and run this algorithm recursively on them.
      • Add the statements inside the defer's block to a finally block, to be placed after the try block from above.

This should produce a translation like this:

// Swift

func f() {
	print("Open 0")
	defer {
		print("Close 0")
	}
	// throwingFunction()
	print("Open 1")
	defer {
		print("Close 1")
	}
	print("Done!")
}
// Kotlin

fun f() {
	println("Open 0")
	try {
		// throwingFunction()
		println("Open 1")
		try {
			println("Done!")
		}
		finally {
			println("Closing 1")
		}
	}
	finally {
		println("Close 0")
	}
}

This translation prints all of the messages in the correct order. It also avoids printing unnecessary messages if we exit early. For instance, if we add a throwing function right before the Open 1 message, we only get the Close 0 message on both platforms.

Support translating unit tests

There's currently some barebones support for translating XCTest which is used for the Bootstrapping Tests. This could maybe be expanded into more solid support for translating XCTest references into a real Android unit test framework like Espresso.

Avoid reporting ranges in the wrong files.

Currently, source file ranges only include information on the lines and columns of a file, but not on the file path. The code assumes the range is relative to the current file being translated, which is almost always the case, but can cause issues when an expression comes from another file (for instance the templates library).

If the file path was included in the range, this could be fixed, but it might cause performance issues.

Xcode error: Command PhaseScriptExecution failed with a nonzero exit code

I get this error when trying to compile the Gryphon target.
The only difference I can think of between my project and what's described in the tutorial is that my xcproj is part of an xcworspace.

In an effort to debug this I created a simple Swift file like this.
Added it to all my targets and verified that they compile correctly.
I'm using Gryphon 0.6

// gryphon output: ../iReal-Pro-Android/iRealPro/src/main/java/com/massimobiolcati/irealb/MixerInstrumentType.kt

import Foundation

class Test {
    var testVar = "test"
}

Build target Gryphon_2020-05-28T15-03-15.txt

Screen Shot 2020-05-28 at 3 05 27 PM

Here is the content of updateASTDumps.sh:

    cd /Users/massimo/Developer/iReal-Pro-iOS-macOS
    export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/bin:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/libexec:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/usr/bin:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/usr/local/bin:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk
    export TOOLCHAINS=com.apple.dt.toolchain.XcodeDefault
	/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc -module-name iReal_Pro -Onone -enable-batch-mode -enforce-exclusivity=checked -DDEBUG -Xfrontend -warn-long-expression-type-checking=500 -Xfrontend -warn-long-function-bodies=1000 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk -target arm64-apple-ios12.4 -g -Xfrontend -serialize-debugging-options -application-extension -embed-bitcode-marker -enable-testing -swift-version 5 -I /Users/massimo/Developer/iReal-Pro-iOS-macOS/build/Debug-iphoneos -F /Users/massimo/Developer/iReal-Pro-iOS-macOS/build/Debug-iphoneos -j20 /Users/massimo/Developer/iReal-Pro-iOS-macOS/Share\ Extension\ iOS/ShareViewController.swift /Users/massimo/Developer/iReal-Pro-iOS-macOS/Shared/Classes/ColorExtensions.swift /Users/massimo/Developer/iReal-Pro-iOS-macOS/Shared/Classes/Logging.swift -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/iReal\ Pro-generated-files.hmap -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/iReal\ Pro-own-target-headers.hmap -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/iReal\ Pro-all-target-headers.hmap -Xcc -iquote -Xcc /Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/iReal\ Pro-project-headers.hmap -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/Debug-iphoneos/include -Xcc -I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk/usr/include/libxml2 -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/DerivedSources-normal/arm64 -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/DerivedSources/arm64 -Xcc -I/Users/massimo/Developer/iReal-Pro-iOS-macOS/build/iReal\ Pro.build/Debug-iphoneos/Share\ Extension\ iOS.build/DerivedSources -Xcc -DDEBUG=1 -working-directory /Users/massimo/Developer/iReal-Pro-iOS-macOS /Users/massimo/Developer/iReal-Pro-iOS-macOS/.gryphon/GryphonTemplatesLibrary.swift -output-file-map /Users/massimo/Developer/iReal-Pro-iOS-macOS/.gryphon/output-file-map.json -dump-ast -D GRYPHON

Specify native collection warnings

Currently, when a native collection is detected, the warning suggest changing it to either a List or a Map. This could be specified to suggesting Maps for Dictionaries and Lists for Arrays.

Support for translating block comments

Currently only line comments (// blabla or /// blabla) are detected as comments for translation. Block comments are ignored. This would involve teaching the compiler to parse block comments (both /* */ and /** **/) and translate them correctly.

Add parentheses around "or" operators in if conditions

Gryphon translates a list of if conditions in Swift as a sequence of "&&" operations in Kotlin. This means the code below

if true || true, false {
	print("Hello!")
}

would translate to

if (true || true && false) {
	println("Hello!")
}

This is a problem because Kotlin evaluates "&&" before it evaluates "||". This means the Swift code above will never print "Hello!", but the Kotlin code will. A solution to this problem would be to automatically add parentheses around "||" conditions, making the desired precedence explicit.

Note that this could also be done for any other operators that have less precedence than "&&" in Kotlin.

Support access modifiers for setters

Both Swift and Kotlin support specifying access modifiers for property setters, which means Gryphon could probably support a translation:

class A {
    internal(set) var a = 0
    private(set) var b = 0
}
class A {
    var a = 0
        internal set
    var b = 0
        private set
}

Translating this feature should also involve calculating the correct access modifier and omitting it when appropriate, just like with any other declaration.

Raise warnings for side effects in if-lets recursively

Gryphon raises warnings when it finds a function call that may cause side-effects to happen in Kotlin but not in Swift:

func f() -> Int? { }

if true, let a = f() {
	if true, let b = f() {
	}
}

gets translated to

internal fun f(): Int? {
}

fun main(args: Array<String>) {
	val a: Int? = f()

	if (true && a != null) {
		val b: Int? = f()
		if (true && b != null) {
		}
	}
}

In this example, Gryphon raises a warning for the first if, but not the second one. This is a bug. It happens because the pass that raises warnings does not visit the first if's statements recursively (to avoid running twice on any possible else statements).

This should be fixed: the pass should run once on the if's statements and once on the else's statements.

Support defers in nested blocks

Defers are currently only supported as top-level statements in function bodies, but they could also be supported inside other, nested blocks (if's, for's, etc). It's important to add acceptance tests to check if the behavior of early exits in each case is the same (i.e. a defer in an if block only exits the if, not the parent function).

Add Swift 5.3 beta in the tests

The Swift 5.3 release branch already exists, which means we can start testing with it to catch any errors early and be ready for when it launches. It might be a good idea to mark it as a development branch in the tests, so that we can check how recent it is and complain if it gets too old (forcing devs to keep it updated, otherwise there's no point in testing it at all).

Support returns in closures

In Swift, return statements inside closures exit the closure; in Kotlin, they exit the parent function. Kotlin offers a way to make returns exit the closure using labels. This could probably be accomplished in Gryphon by automatically adding return@closureName, where closureName is the name of the method that called the topmost closure. For instance:

fun foo(): List<List<Int>> {
    return listOf(1, 2, 3, 4, 5).map { element ->
        listOf(1, 2, 3, 4, 5).filter {
            return@filter (it <= element)
        }
    }
}

It's important to make sure there are no conflicts with closures having the same label (i.e. two nested maps).

Match templates for trailing closures in functions with multiple parameters

Currently, templates for trailing closures only match if the closure is the only parameter of the function call. If there are more, the template won't match. For instance:

// Define a function with multiple parameters
func f(int: Int, a: (Int) -> ()) {
	a(int)
}

// Define the template
func gryphonTemplates() {
	let _closure: (Int) -> () = { _ in }
	let _int: Int = 0

	f(int: _int, a: _closure)
	"g(_int) _closure"
}

// Call the function
f(int: 0) { print($0) }

translates to

internal fun f(int: Int, a: (Int) -> Unit) {
	a(int)
}

fun main(args: Array<String>) {
	f(int = 0, a = { println(it) })
}

If the template had matched, the translation (in the second-to-last line of Kotlin code) would be g(0) { println(it) } as the template said.

This template works if the f function only has one parameter (i.e. if we removed the int parameter), but not if it has more than one.

Support optional function type for variables

Describe the bug
When using optional function type the result was not an optional function type, instead it was a function type with optional return in Kotlin

How to reproduce it
Create this code in Swift
public var foo: (() -> ())?

Will output this code in Kotlin
var foo: () -> Unit? = null

The proper result should be
var foo: (() -> Unit)? = null

Your environment (please complete the following information):

  • OS: macOS
  • Gryphon version: 0.7

Ignore comments in multiline strings

Comments that are inside multiline strings (as a part of the string itself) are being treated as normal code comments and are being duplicated as comment nodes. For example:

// gryphon multiline
let a = """

// blabla

"""

...is translated as...

// gryphon multiline
internal val a: String = """
// blabla
"""

// blabla

Note the // blabla comment at the end of the Kotlin translation that shouldn't be there.

This only seems so happen when Gryphon is invoked with --no-main-file, since the comment handling for main files is different.

The `equals` and `toString` translations are open regardless of their parent classes

The translations for == and description are done manually and always include the open modifier. This is a problem when the enveloping class is marked as final, which causes Kotlin to raise a warning (since these methods can't be overridden anyway).

It would be better to set the isOpen property instead of adding the open keyword manually so it could be treated correctly. In this case, it's important to check if the transpilation pass that handles open/final comes after the one that translates == and description.

Test multiline strings

Add a test case to check if multiline strings are being translated correctly. Include tests for edge cases (newlines at the start and at the end, different indentations, comments with "//", etc).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.