This is a catalog of translations by SwiftScript, a transpiler from Swift to JavaScript, for a hackathon. It supports only a subset of Swift and has some incompatibilities.
SwiftScript tries to translate Swift codes to JS codes using builtin JS APIs. However some needs a supporting library to simulate behaviors of Swift. A typical example is ?.
.
If
// Swift
foo?.bar
is translated to
// JavaScript (ES6)
(foo == null ? null : foo.bar)
, it causes a problem when foo
is a complex or mutating expression. Introducing a function q
(as ?
) resolves it.
// JavaScript (ES6)
q(foo, foo => foo.bar)
// Swift
var a: Int? = 42
// JavaScript (ES6)
let a = 42;
// Swift
let a = 42
// JavaScript (ES6)
const a = 42;
// Swift
func foo(x: Int, y: String) -> T {
...
}
// JavaScript (ES6)
function foo(x, y) {
...
}
// Swift
class Foo {
var x: Int
let y: String
var z: Bool = true
init(x: Int, y: String) {
self.x = x
self.y = y
}
}
// JavaScript (ES6)
class Foo {
constructor(x, y) {
this.x = x;
this.y = y;
this.z = true;
}
}
// Swift
class Foo {
func foo(x: Int, y: String) -> Foo {
...
}
}
// JavaScript (ES6)
class Foo {
foo(x, y) {
...
}
}
// Swift
class Foo {
private var _x: Int = 0
var x: Int {
get {
return self._x
}
set {
print(self.x)
self._x = newValue
}
}
}
// JavaScript (ES6)
class Foo {
constructor() {
this._x = 0;
}
get x() {
return this._x;
}
set x(newValue) {
console.log(this.x);
this._x = newValue;
}
}
// Swift
class Bar: Foo {
override init() {
super.init()
}
override func foo(x: Int, y: String) -> Foo {
...
}
}
// JavaScript (ES6)
class Bar extends Foo {
constructor(x, y) {
super();
}
foo(x, y) {
...
}
}
// Swift
class Foo {
static func foo(x: Int, y: String) -> Foo {
...
}
}
// JavaScript (ES6)
class Foo {
static foo(x, y) {
...
}
}
Unsupported.
Unsupported.
// Swift
protocol Foo {
...
}
// JavaScript (ES6)
Just ignores protocols. Be aware that protocol names are required in ASTs to distinguish if a type should be contained in extends
clause. Only classes should be contained there in JS.
// Swift
if foo {
...
} else if bar {
...
} else {
...
}
// JavaScript (ES6)
if (foo) {
...
} else if (bar) {
...
} else {
...
}
// Swift
guard foo else {
...
}
// JavaScript (ES6)
if (!(foo)) {
...
}
// Swift
switch foo {
case 1:
...
case 2:
...
return
case 3:
break
case 4:
...
fallthrough
default:
...
}
// JavaScript (ES6)
switch (foo) {
case 1:
...
break;
case 2:
...
return;
case 3:
break;
case 4:
...
default:
...
break;
}
Unsupported.
// Swift
while foo {
...
}
// JavaScript (ES6)
while (foo) {
...
}
// Swift
repeat {
...
} while foo
// JavaScript (ES6)
do {
...
} while (foo);
// Swift
for foo in foos {
...
}
// JavaScript (ES6)
for (foo of foos) {
...
}
// Swift
for i in 0..<42 {
...
}
// JavaScript (ES6)
for (let i = 0; i < 42; i++) {
...
}
// Swift
for i in 0...42 {
...
}
// JavaScript (ES6)
for (let i = 0; i < 43; i++) {
...
}
// Swift
do {
...
}
// JavaScript (ES6)
{
...
}
// Swift
return foo
// JavaScript (ES6)
return foo;
// Swift
break
// JavaScript (ES6)
break;
// Swift
continue
// JavaScript (ES6)
continue;
// Swift
do {
...
} catch let error {
...
}
// JavaScript (ES6)
try {
...
} catch (error) {
...
}
// Swift
do {
...
} catch let error as FooError {
...
} catch let error as BarError {
...
} catch let error {
...
}
// JavaScript (ES6)
try {
...
} catch (error) {
if (error instanceof FooError) {
...
} else if (error instanceof BarError) {
...
} else {
...
}
}
// Swift
throw foo
// JavaScript (ES6)
throw foo;
Unsupported.
// Swift
{ (x: Int, y: String) -> Foo in
...
return ...
}
// JavaScript (ES6)
(x, y) => {
...
return ...;
}
// Swift
{ (x: Int, y: String) -> Foo in foo(x) }
// JavaScript (ES6)
(x, y) => foo(x)
// Swift
{ (x: Int) -> Foo in
...
return ...
}
// JavaScript (ES6)
x => {
...
return ...;
}
// Swift
{ foo($0, $1) }
// JavaScript (ES6)
($0, $1) => foo($0, $1)
$0
, $1
and so on are valid identifiers in JS.
// Swift
foo(bar: 42, baz: "xyz")
// JavaScript (ES6)
foo(42, "xyz")
Labels are just ignored in JS.
// Swift
Foo(x: 42, y: "xyz")
// JavaScript (ES6)
new Foo(42, "xyz")
// Swift
foo(x) { a, b in bar(a, b) }
foo { a, b in bar(a, b) }
// JavaScript (ES6)
foo(x, (a, b) => foo(a, b))
foo((a, b) => foo(a, b))
Basically common in both languages.
// Swift
foo[bar]
// JavaScript (ES6)
foo[bar]
Subscripts for custom types are not supported.
// Swift
self
// JavaScript (ES6)
this
Common in both languages.
// Swift
(foo)
// JavaScript (ES6)
(foo)
// Swift
(2, 3, 5)
// JavaScript (ES6)
[2, 3, 5];
// Swift
let (x, y, z) = (2, 3, 5)
// JavaScript (ES6)
const [x, y, z] = [2, 3, 5];
// Swift
.some(42)
Unsupported.
// Swift
_
Unsupported.
Although _
is a valid identifier in JS, redeclaring _
causes a syntax error.
// Swift
foo?.bar
// JavaScript (ES6)
q(foo, foo => foo.bar)
q
is a function in the supporting library.
// Swift
try foo()
// JavaScript (ES6)
foo()
// Swift
try! foo()
// JavaScript (ES6)
tryx(() => foo())
tryx
is a function in the supporting library.
// Swift
try? foo()
// JavaScript (ES6)
tryq(() => foo())
tryq
is a function in the supporting library.
Most operators are common in both languages. This subsection lists up only operators which needs some translations.
// Swift
foo ?? bar
// JavaScript (ES6)
qq(foo, bar)
qq
is a function in the supporting library.
// Swift
foo is Foo
// JavaScript (ES6)
foo instanceof Foo
// Swift
foo as Foo
// JavaScript (ES6)
foo
// Swift
foo as Foo
// JavaScript (ES6)
asq(foo, foo => foo instanceof Foo)
asq
is a function in the supporting library.
// Swift
foo as Foo
// JavaScript (ES6)
asx(foo, foo => foo instanceof Foo)
asx
is a function in the supporting library.
// Swift
1...10
0..<10
// JavaScript (ES6)
range(1, 11)
range(0, 10)
range
is a function in the supporting library, which should be implemented function*
and yield
(References: function*, range implementation by legacy generator function).
Because it does not support precedencegroup
and custom operators for the present, precedences of operators follow ones of Swift 2.
References: Swift 2, JavaScript
Swift
Precedence | Operators |
---|---|
160 | << , >> , * , / , % , & |
140 | + , - , ` |
135 | ... , ..< |
132 | is , as , as? , as! |
131 | ?? |
130 | < , <= , > , >= , == , != , === , !== |
120 | && |
110 | ` |
100 | ?: |
90 | = , += , -= , *= , /= , %= , <<= , >>= , &= , ` |
JavaScript
Precedence | Operators |
---|---|
14 | * , / , % |
13 | + , - |
12 | << , >> |
11 | < , <= , > , >= , instanceof |
10 | == , != , === , !== |
9 | & |
8 | ^ |
7 | ` |
6 | && |
5 | ` |
4 | ?: |
3 | = , += , -= , *= , /= , %= , <<= , >>= , &= , ` |
Some operators have a lower precedence in JS than in Swift. They should be used in ()
. It is also possible to use all operators in ()
to simulate those precedences perfectly. However it results redundant ()
s like ((2 + 3) + 5)
for 2 + 3 + 5
. To prevent those redundant ()
s, we add an extra ()
for only <<
, >>
, &
, |
, ^
, instanceof
.
// Swift
foo << bar
// JavaScript (ES6)
(foo << bar)
Although their precedences in JS are slightly different from ones in Swift, they seems not to cause problems in most cases. Combinations of operators like <
and ones like ==
appear rarely.
Prefix +
, -
, !
and ~
are common in both languages. This subsection lists up only operators which needs some translations.
// Swift
foo!
// JavaScript (ES6)
x(foo)
x
is a function in the supporting library.
It is common in both languages.
Unsupported.
Unsupported.
Does not support ExpressibleBy*
. Then most literals in both languages are common.
// Swift
123
123.0
true
false
// JavaScript (ES6)
123
123.0
true
false
// Swift
nil
// JavaScript (ES6)
null
Strictly speaking, they are not equivalent because null
cannot be nested. Int??
in Swift can distinguish .none
or .some(.none)
while null
in JS cannot.
In this hackathon, we do not support such behaviors of Optional
s in Swift.
String literals are basically common in both languages.
// Swift
"xyz"
// JavaScript (ES6)
"xyz"
Special characters in string literals are basically common in both languages.
References: Swift, JavaScript
// Swift
"xyz\(foo)"
// JavaScript (ES6)
`xyz${foo}`
// Swift
[foo, bar, baz]
// JavaScript (ES6)
[foo, bar, baz]
Strictly speaking, they are not equivalent because Array
is a value type in Swift and a reference type in JS.
In this hackathon, we ignores such a difference.
// Swift
[
"foo": 2,
"bar": 3,
"baz": 5
]
// JavaScript (ES6)
{
"foo": 2,
"bar": 3,
"baz": 5
}
Strictly speaking, they are not equivalent because Dictionary
in Swift is a value type and Object
in JS is a reference type. Object
in JS also does not support key types except String
.
In this hackathon, we ignores such a difference.
Just ignores inside <>
.
// Swift
func foo<T: Sequence, U>(_ x: T) -> U where T.Iterator.Element == Int, U: Equatable {
...
}
// JavaScript (ES6)
function foo(x) {
...
}
// Swift
class Foo<T: Sequence, U> where T.Iterator.Element == Int, U: Equatable {
...
}
// JavaScript (ES6)
class Foo {
...
}
// Swift
foo<Bar<Baz>>(bar)
// JavaScript (ES6)
foo(bar)
Unsupported.