Skip to content

Commit

Permalink
Merge pull request #71 from angelolloqui/feature/v0.1.4
Browse files Browse the repository at this point in the history
Feature/v0.1.4
  • Loading branch information
angelolloqui authored Mar 24, 2018
2 parents dbeb813 + 8d6c593 commit 06bfa56
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 55 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,15 @@ The project is in active development, with many rules and improvements still to
- [x] Basic memory management (`weak`, captures,...)
- [x] Function returns and named parameters
- [x] Basic property transfromers (getters and setters)
- [ ] Advance property transformers (`lazy`, `didSet`,...)
- [x] Lazy properties
- [ ] Properties with `didSet`/`willSet`
- [x] Static to Companion
- [x] Struct to data class
- [x] String interpolators
- [x] Foundation types (arrays, maps,...)
- [x] Basic exception handling syntax
- [x] Simple enums
- [ ] Complex enum cases to Sealed classes
- [x] Complex enum cases to Sealed classes
- [ ] ... [(check open issues)](https://github.com/angelolloqui/SwiftKotlin/issues)

With the implemented rules you can already get pretty decent Kotlin output for many of your classes. The rest will come in future releases.
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftKotlinApp/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.3</string>
<string>0.1.4</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftKotlinCommandLine/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let kotlinTokenizer = KotlinTokenizer(
FoundationMethodsTransformPlugin()
]
)
let version = "0.1.3"
let version = "0.1.4"
let arguments = [
"output",
"help",
Expand All @@ -25,7 +25,7 @@ let arguments = [

func showHelp() {
print("swiftkotlin, version \(version)")
print("copyright (c) 2017 Angel G. Olloqui")
print("copyright (c) 2018 Angel G. Olloqui")
print("")
print("usage: swiftkotlin [<file>] [--output path]")
print("")
Expand Down
148 changes: 113 additions & 35 deletions Sources/SwiftKotlinFramework/KotlinTokenizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,16 @@ public class KotlinTokenizer: SwiftTokenizer {
let defaultTokens = parameter.defaultArgumentClause.map {
return parameter.newToken(.symbol, " = ", node) + tokenize($0)
}
let varargsTokens = parameter.isVarargs ? [parameter.newToken(.symbol, "...", node)] : []
let varargsTokens = parameter.isVarargs ? [
parameter.newToken(.keyword, "vararg", node),
parameter.newToken(.space, " ", node),
] : []

return
varargsTokens +
nameTokens +
typeAnnoTokens +
defaultTokens +
varargsTokens
typeAnnoTokens +
defaultTokens
}

open override func tokenize(_ result: FunctionResult, node: ASTNode) -> [Token] {
Expand Down Expand Up @@ -104,7 +107,8 @@ public class KotlinTokenizer: SwiftTokenizer {
declaration.members.forEach { member in
if member.isStatic {
staticMembers.append(member)
} else if member.declaration is ConstantDeclaration || member.declaration is VariableDeclaration {
} else if member.declaration is ConstantDeclaration ||
(member.declaration as? VariableDeclaration)?.initializerList != nil {
declarationMembers.append(member)
} else {
otherMembers.append(member)
Expand Down Expand Up @@ -220,7 +224,7 @@ public class KotlinTokenizer: SwiftTokenizer {

open override func tokenize(_ modifier: DeclarationModifier, node: ASTNode) -> [Token] {
switch modifier {
case .static, .unowned, .unownedSafe, .unownedUnsafe, .weak, .convenience, .dynamic:
case .static, .unowned, .unownedSafe, .unownedUnsafe, .weak, .convenience, .dynamic, .lazy:
return []
default:
return super.tokenize(modifier, node: node)
Expand Down Expand Up @@ -257,54 +261,92 @@ public class KotlinTokenizer: SwiftTokenizer {
memberTokens
].joined(token: declaration.newToken(.linebreak, "\n"))
}

open override func tokenize(_ declaration: VariableDeclaration) -> [Token] {
var tokens = super.tokenize(declaration)

let readOnly: Bool
switch declaration.body {
case .codeBlock: readOnly = true
case .getterSetterBlock(_, _, let block) where block.setter == nil: readOnly = true
default: readOnly = false
}

if readOnly {
tokens = tokens.replacing({ $0.value == "var" }, with: [declaration.body.newToken(.keyword, "val", declaration)], amount: 1)
}

let spaceToken = declaration.newToken(.space, " ")
let mutabilityTokens = [declaration.newToken(.keyword, declaration.isReadOnly ? "val" : "var")]
let attrsTokenGroups = declaration.attributes.map { tokenize($0, node: declaration) }
var modifierTokenGroups = declaration.modifiers.map { tokenize($0, node: declaration) }
var bodyTokens = tokenize(declaration.body, node: declaration)

if declaration.isImplicitlyUnwrapped {
tokens.insert(contentsOf: [
declaration.newToken(.keyword, "lateinit"),
declaration.newToken(.space, " ")
], at: 0)
}
else if declaration.isOptional {
if declaration.initializerList?.last?.initializerExpression == nil {
tokens += [
declaration.newToken(.space, " "),
modifierTokenGroups = [[declaration.newToken(.keyword, "lateinit")]] + modifierTokenGroups
}

if declaration.isOptional && declaration.initializerList?.last?.initializerExpression == nil {
bodyTokens = bodyTokens + [
spaceToken,
declaration.newToken(.symbol, "="),
declaration.newToken(.space, " "),
spaceToken,
declaration.newToken(.keyword, "null")
]
} else if declaration.isLazy {
bodyTokens = bodyTokens
.replacing({ $0.value == " = " }, with: [
spaceToken,
declaration.newToken(.keyword, "by"),
spaceToken,
declaration.newToken(.keyword, "lazy"),
spaceToken,
], amount: 1)
if bodyTokens.last?.value == ")" {
bodyTokens.removeLast()
}
if bodyTokens.last?.value == "(" {
bodyTokens.removeLast()
}
}
return tokens

return [
attrsTokenGroups.joined(token: spaceToken),
modifierTokenGroups.joined(token: spaceToken),
mutabilityTokens,
bodyTokens
].joined(token: spaceToken)
}

open override func tokenize(_ body: VariableDeclaration.Body, node: ASTNode) -> [Token] {
let getterTokens = [
body.newToken(.keyword, "get()", node),
body.newToken(.space, " ", node)
]
switch body {
case let .codeBlock(name, typeAnnotation, codeBlock):
let getterTokens = [
body.newToken(.keyword, "get()", node),
body.newToken(.space, " ", node)
]
return body.newToken(.identifier, name, node) +
tokenize(typeAnnotation, node: node) +
body.newToken(.linebreak, "\n", node) +
indent(
getterTokens +
tokenize(codeBlock)
)

case let .willSetDidSetBlock(name, typeAnnotation, initExpr, block):
let newName = block.willSetClause?.name ?? "newValue"
let oldName = block.didSetClause?.name ?? "oldValue"
let fieldAssignmentExpression = AssignmentOperatorExpression(
leftExpression: IdentifierExpression(kind: IdentifierExpression.Kind.identifier("field", nil)),
rightExpression: IdentifierExpression(kind: IdentifierExpression.Kind.identifier(newName, nil))
)
let oldValueAssignmentExpression = ConstantDeclaration(initializerList: [
PatternInitializer(pattern: IdentifierPattern(identifier: oldName),
initializerExpression: IdentifierExpression(kind: IdentifierExpression.Kind.identifier("field", nil)))
])
let setterCodeBlock = CodeBlock(statements:
(block.didSetClause?.codeBlock.statements.count ?? 0 > 0 ? [oldValueAssignmentExpression] : []) +
(block.willSetClause?.codeBlock.statements ?? []) +
[fieldAssignmentExpression] +
(block.didSetClause?.codeBlock.statements ?? [])
)
let setterTokens = tokenize(GetterSetterBlock.SetterClause(name: newName, codeBlock: setterCodeBlock), node: node)
let typeAnnoTokens = typeAnnotation.map { tokenize($0, node: node) } ?? []
let initTokens = initExpr.map { body.newToken(.symbol, " = ", node) + tokenize($0) } ?? []
return [
body.newToken(.identifier, name, node)] +
typeAnnoTokens +
initTokens +
[body.newToken(.linebreak, "\n", node)] +
indent(setterTokens)

default:
return super.tokenize(body, node: node).removingTrailingSpaces()
}
Expand All @@ -329,6 +371,25 @@ public class KotlinTokenizer: SwiftTokenizer {
return super.tokenize(newSetter, node: node)
}

open override func tokenize(_ block: WillSetDidSetBlock, node: ASTNode) -> [Token] {
let name = block.willSetClause?.name ?? block.didSetClause?.name ?? "newValue"
let willSetBlock = block.willSetClause.map { tokenize($0.codeBlock) }?.tokensOnScope(depth: 1) ?? []
let didSetBlock = block.didSetClause.map { tokenize($0.codeBlock) }?.tokensOnScope(depth: 1) ?? []
let assignmentBlock = [
block.newToken(.identifier, "field", node),
block.newToken(.keyword, " = ", node),
block.newToken(.identifier, name, node)
]
return [
[block.newToken(.startOfScope, "{", node)],
willSetBlock,
indent(assignmentBlock),
didSetBlock,
[block.newToken(.endOfScope, "}", node)]
].joined(token: block.newToken(.linebreak, "\n", node))

}

open override func tokenize(_ declaration: ImportDeclaration) -> [Token] {
return []
}
Expand Down Expand Up @@ -634,6 +695,23 @@ public class KotlinTokenizer: SwiftTokenizer {
with: arrowTokens,
amount: 1)
}

// Last return can be removed
if let lastReturn = expression.statements?.last as? ReturnStatement,
let index = tokens.index(where: { $0.node === lastReturn && $0.value == "return" }) {
tokens.remove(at: index)
tokens.remove(at: index)
}

// Other returns must be suffixed with call name
if let callExpression = expression.lexicalParent as? FunctionCallExpression,
let memberExpression = callExpression.postfixExpression as? ExplicitMemberExpression {
while let returnIndex = tokens.index(where: { $0.value == "return" }) {
tokens.remove(at: returnIndex)
tokens.insert(expression.newToken(.keyword, "return@"), at: returnIndex)
tokens.insert(expression.newToken(.identifier, memberExpression.identifier), at: returnIndex + 1)
}
}
return tokens
}

Expand Down
39 changes: 39 additions & 0 deletions Sources/SwiftKotlinFramework/utils/AST+Operations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ extension VariableDeclaration {
return typeAnnotation?.type is OptionalType
}

var isReadOnly: Bool {
switch body {
case .codeBlock: return true
case .getterSetterBlock(_, _, let block) where block.setter == nil: return true
default: return isLazy
}
}

var isLazy: Bool {
return modifiers.isLazy
}

var typeAnnotation: TypeAnnotation? {
return initializerList?
.flatMap { $0.pattern as? IdentifierPattern }
Expand All @@ -35,6 +47,7 @@ extension VariableDeclaration {
return nil
}
}

}

extension FunctionDeclaration {
Expand Down Expand Up @@ -101,6 +114,10 @@ extension Collection where Iterator.Element == DeclarationModifier {
var isStatic: Bool {
return self.contains(where: { $0.isStatic })
}

var isLazy: Bool {
return self.contains(where: { $0.isLazy })
}
}

extension DeclarationModifier {
Expand All @@ -110,6 +127,13 @@ extension DeclarationModifier {
default: return false
}
}

var isLazy: Bool {
switch self {
case .lazy: return true
default: return false
}
}
}

extension SuperclassExpression {
Expand All @@ -134,6 +158,21 @@ extension SelfExpression {
}
}

extension ExplicitMemberExpression {
var identifier: String {
switch kind {
case let .tuple(_, index):
return "var\(index)"
case let .namedType(_, identifier):
return identifier
case let .generic(_, identifier, _):
return identifier
case let .argument(_, identifier, _):
return identifier
}
}
}

extension EnumDeclaration.Member {

var unionStyleEnumCase: EnumDeclaration.UnionStyleEnumCase? {
Expand Down
16 changes: 16 additions & 0 deletions Sources/SwiftKotlinFramework/utils/Token+Operations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ extension Array where Iterator.Element == Token {
return tokens
}

public func tokensOnScope(depth: Int) -> [Token] {
var tokens = [Token]()
var scope = 0
for token in self {
if token.kind == .endOfScope {
scope -= 1
}
if scope == depth {
tokens.append(token)
}
if token.kind == .startOfScope {
scope += 1
}
}
return tokens
}
}

extension ASTTokenizable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ fun tokenize(codeBlock: String?) : List<String> {

public fun <T> whenAll(promises: List<Promise<T>>) : Promise<List<T>> =
Promise<T>()

fun sumOf(vararg numbers: Int) : Int {
var sum = 0
for (number in numbers) {
sum += number
}
return sum
}
sumOf(42, 597, 12)
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ func tokenize(_ codeBlock: String?) -> [String] {
public func whenAll<T>(promises: [Promise<T>]) -> Promise<[T]> {
return Promise<T>()
}

func sumOf(_ numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf(42, 597, 12)
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ item.selectCallback = { option ->
}
item.selectCallback?.invoke(option)
item.selectCallback!!.invoke(option)
ints.map {
if (it == 0) {
return@map "zero"
}
"non zero"
}
Loading

0 comments on commit 06bfa56

Please sign in to comment.