Skip to content

Commit 8b94a81

Browse files
committed
#14 #34 Added very basic version of Foundation transformer
1 parent b4c24db commit 8b94a81

File tree

10 files changed

+126
-6
lines changed

10 files changed

+126
-6
lines changed

Sources/SwiftKotlinApp/ViewController.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ class ViewController: NSViewController {
1515
let swiftTokenizer = SwiftTokenizer()
1616
let kotlinTokenizer = KotlinTokenizer(
1717
tokenTransformPlugins: [
18-
XCTTestToJUnitTokenTransformPlugin()
18+
XCTTestToJUnitTokenTransformPlugin(),
19+
FoundationMethodsTransformPlugin()
1920
]
2021
)
2122

Sources/SwiftKotlinCommandLine/main.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
import Foundation
1010
import SwiftKotlinFramework
1111

12-
let kotlinTokenizer = KotlinTokenizer()
12+
let kotlinTokenizer = KotlinTokenizer(
13+
tokenTransformPlugins: [
14+
XCTTestToJUnitTokenTransformPlugin(),
15+
FoundationMethodsTransformPlugin()
16+
]
17+
)
1318
let version = "0.1.0"
1419
let arguments = [
1520
"output",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// FoundationMethodsTransformPlugin.swift
3+
// SwiftKotlinPackageDescription
4+
//
5+
// Created by Angel Luis Garcia on 10/12/2017.
6+
//
7+
8+
import Foundation
9+
import Transform
10+
import AST
11+
12+
public class FoundationMethodsTransformPlugin: TokenTransformPlugin {
13+
public var name: String {
14+
return "Foundation transformations"
15+
}
16+
17+
public var description: String {
18+
return "Transforms methods on lists, maps,... like `first`, `count`... to their Kotlin variant"
19+
}
20+
21+
public init() {}
22+
23+
public func transform(tokens: [Token], topDeclaration: TopLevelDeclaration) throws -> [Token] {
24+
var newTokens = [Token]()
25+
26+
for token in tokens {
27+
if token.kind == .identifier,
28+
let memberExpression = token.origin as? ExplicitMemberExpression,
29+
case ExplicitMemberExpression.Kind.namedType(let expression, let identifier) = memberExpression.kind,
30+
let inferredType = inferTypeFor(expression: expression, topDeclaration: topDeclaration),
31+
let replace = memberStringMappings[inferredType]?[identifier] {
32+
newTokens.append(memberExpression.newToken(.identifier, replace))
33+
} else if token.kind == .identifier, token.value == "fatalError", let origin = token.origin, let node = token.node {
34+
newTokens.append(origin.newToken(.keyword, "throw", node))
35+
newTokens.append(origin.newToken(.space, " ", node))
36+
newTokens.append(origin.newToken(.identifier, "Exception", node))
37+
} else {
38+
newTokens.append(token)
39+
}
40+
}
41+
42+
return newTokens
43+
}
44+
45+
func inferTypeFor(expression: PostfixExpression, topDeclaration: TopLevelDeclaration) -> String? {
46+
// TODO: Right now there is no way to infer types. Will be fixed in future versions of AST
47+
return "List"
48+
}
49+
50+
let memberStringMappings = [
51+
"List": [
52+
"first": "firstOrNull()",
53+
"last": "lastOrNull()",
54+
"count": "size",
55+
"isEmpty": "isEmpty()"
56+
],
57+
"String": [
58+
"count": "length",
59+
"uppercased": "toUpperCase",
60+
"lowercased": "toLowerCase"
61+
]
62+
]
63+
64+
// TODO: How to map regex expressions that affect multiple tokens?
65+
let memberRegexMappings = [
66+
"List": [
67+
"index(of: \\(.+))": "indexOf($1)",
68+
"append(\\(.+))": "add($1)",
69+
"remove(at: \\(.+))": "removeAt($1)",
70+
"sorted(by: \\(.+))": "sortedWith(comparator = Comparator($1))",
71+
"joined(separator: \\(.+))": "joinToString(separator = $1)"
72+
]
73+
]
74+
}

SwiftKotlin.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
789D79041F927B63009ED628 /* AST+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 789D79011F927A66009ED628 /* AST+Operations.swift */; };
7171
78CAB0CF1F92808E009E2608 /* XCTTestToJUnitTokenTransformPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CAB0CE1F92808E009E2608 /* XCTTestToJUnitTokenTransformPlugin.swift */; };
7272
78CAB0D61F92843C009E2608 /* TransformPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CAB0D51F92843C009E2608 /* TransformPluginTests.swift */; };
73+
78FE79801FDDABC000B64A2C /* FoundationMethodsTransformPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78FE797E1FDDAB5900B64A2C /* FoundationMethodsTransformPlugin.swift */; };
7374
OBJ_265 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
7475
OBJ_271 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* Package.swift */; };
7576
OBJ_277 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_230 /* Package.swift */; };
@@ -1027,6 +1028,7 @@
10271028
78CAB0CE1F92808E009E2608 /* XCTTestToJUnitTokenTransformPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTTestToJUnitTokenTransformPlugin.swift; sourceTree = "<group>"; };
10281029
78CAB0D41F9283D6009E2608 /* Tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Tests; sourceTree = "<group>"; };
10291030
78CAB0D51F92843C009E2608 /* TransformPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransformPluginTests.swift; sourceTree = "<group>"; };
1031+
78FE797E1FDDAB5900B64A2C /* FoundationMethodsTransformPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoundationMethodsTransformPlugin.swift; sourceTree = "<group>"; };
10301032
OBJ_100 /* AssignmentOperatorExpression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssignmentOperatorExpression.swift; sourceTree = "<group>"; };
10311033
OBJ_101 /* BinaryOperatorExpression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryOperatorExpression.swift; sourceTree = "<group>"; };
10321034
OBJ_102 /* ClosureExpression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosureExpression.swift; sourceTree = "<group>"; };
@@ -1444,6 +1446,7 @@
14441446
children = (
14451447
789D78F91F92792E009ED628 /* TransformPlugin.swift */,
14461448
78CAB0CE1F92808E009E2608 /* XCTTestToJUnitTokenTransformPlugin.swift */,
1449+
78FE797E1FDDAB5900B64A2C /* FoundationMethodsTransformPlugin.swift */,
14471450
);
14481451
path = plugins;
14491452
sourceTree = "<group>";
@@ -2501,6 +2504,7 @@
25012504
buildActionMask = 0;
25022505
files = (
25032506
78314DD61FC8B64700539A2D /* TokenizationResult.swift in Sources */,
2507+
78FE79801FDDABC000B64A2C /* FoundationMethodsTransformPlugin.swift in Sources */,
25042508
OBJ_358 /* KotlinTokenizer.swift in Sources */,
25052509
789D78FB1F927957009ED628 /* TransformPlugin.swift in Sources */,
25062510
OBJ_360 /* TransformOptions.swift in Sources */,

Tests/SwiftKotlinFrameworkTests/KotlinTokenizerTests.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import XCTest
99
import SwiftKotlinFramework
1010

1111
class KotlinTokenizerTests: XCTestCase {
12-
let kotlinTokenizer = KotlinTokenizer()
12+
let kotlinTokenizer = KotlinTokenizer(
13+
tokenTransformPlugins: [
14+
XCTTestToJUnitTokenTransformPlugin(),
15+
FoundationMethodsTransformPlugin()
16+
]
17+
)
1318

1419
func testAll() {
1520
let files = try! FileManager().contentsOfDirectory(atPath: self.testFilePath)

Tests/SwiftKotlinFrameworkTests/Tests/KotlinTokenizer/control_flow.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ val obj = obj as? Movie
1818
if (obj != null) {}
1919
val movie = obj2 as? Movie
2020
if (movie != null) {}
21-
if (numbers.flatMap({ it % 2 }).count == 1) {}
21+
if (numbers.flatMap({ it % 2 }).size == 1) {}
2222
for (current in someObjects) {}
2323
for (i in 0 until count) {}
2424
for (i in 1 .. 3) {}

Tests/SwiftKotlinFrameworkTests/Tests/KotlinTokenizer/expressions.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ val label = if (x > 0) "Positive" else "negative"
44
button.color = if (item.deleted) red else green
55
val text = label ?: "default"
66
service.deleteObject()
7-
this.service.fetchData()?.user?.name?.count
8-
this.data.filter { it.item?.value == 1 }.map { it.key }.first?.name?.count
7+
this.service.fetchData()?.user?.name?.size
8+
this.data.filter { it.item?.value == 1 }.map { it.key }.firstOrNull()?.name?.size
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
val list = listOf(1, 2, 3)
2+
list.firstOrNull()
3+
list.lastOrNull()
4+
list.size
5+
list.isEmpty()
6+
throw Exception("An error ${error} occurred")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
let list = [1, 2, 3]
2+
3+
list.first
4+
list.last
5+
list.count
6+
list.isEmpty
7+
//list.index(of: a) --> list.indexOf(a)
8+
//list.append(element) --> list.add(element)
9+
//list.remove(at: index) --> list.removeAt(index)
10+
//list.sorted(by: lambda) --> sortedWith(comparator = Comparator(lambda))
11+
//list.joined(separator: joiner) --> elements. joinToString(separator = joiner)
12+
13+
//let string = "A string"
14+
//string.count --> string.length
15+
//string.uppercased() --> string.toUpperCase()
16+
//string.lowercased() --> string.toLowerCase()
17+
18+
fatalError("An error \(error) occurred")
19+

Tests/SwiftKotlinFrameworkTests/TransformPluginTests.swift

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ class TransformPluginTests: XCTestCase {
1616
file: "XCTTestToJUnitTokenTransformPlugin")
1717
}
1818

19+
func testFoundationTransformPlugin() {
20+
try! testTokenTransformPlugin(
21+
plugin: FoundationMethodsTransformPlugin(),
22+
file: "FoundationMethodsTransformPlugin")
23+
}
24+
1925
}
2026

2127
extension TransformPluginTests {

0 commit comments

Comments
 (0)