Skip to content

Instantly share code, notes, and snippets.

@lucaswerkmeister
Last active August 23, 2016 18:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucaswerkmeister/64085bbf2ae17f702a0abdb46a5e3daf to your computer and use it in GitHub Desktop.
Save lucaswerkmeister/64085bbf2ae17f702a0abdb46a5e3daf to your computer and use it in GitHub Desktop.
Google Summer of Code 2016 final evaluation

Ceylon TypeScript loader

Original proposal

The original goal was to finish the first iteration of the program, and to get as far as possible with the second iteration, including necessary changes to the compiler.

As explained in the proposal, both iterations produce a Ceylon module (i. e., modname/modver/modname-modver{,-model}.js{,.sha1} files) in a Ceylon module repository (e. g. modules/). This module can then be used like any other Ceylon module – moved between repositories, imported from a module descriptor, etc.

The first iteration is done and may be found at github:lucaswerkmeister/ctsl (head commit at the time of final submission: 8e91b1a). Occasionally, small additional changes are required, but it can load the TypeScript compiler well enough that the second iteration can be written. In particular, it supports toplevel values and functions, classes, interfaces (including index and call signatures), and enums. Declarations may also be generic, though only some hard-coded type parameter names and references to some hard-coded generic types are supported. You can build the first iteration with the commands in the section “get CTSL (first iteration)” of the setup script farther down, replacing the make target install with ctsl.js. Afterwards, you can change to the ctsl directory and run ctsl.js with Node.js. For example, suppose you have the following TypeScript module in a file leftpad.ts:

function leftpad(s: string, length: number, c: string = ' '): string {
    while (s.length < length) s = c + s;
    return s;
}

You can convert it to a Ceylon module with the following command:

node ctsl.js leftpad 1 leftpad.ts # name version file...

Afterwards, you can use that module from Ceylon:

// module.ceylon
module mymodule "1" {
    import leftpad "1";
    import ceylon.test "1.2.2";
}
// run.ceylon
import leftpad { leftpad }
import ceylon.test { ... }

test shared void testLeftpad() {
    assertEquals {
        expected = "  abc";
        actual = leftpad { "abc"; length = 5; c = " "; };
    };
    assertEquals {
        expected = "oozing";
        actual = leftpad { "zing"; length = "oozing".size; c = "o"; };
    };
}
ceylon compile-js,run-js mymodule

(Note that module names with multiple components, like ceylon.test or foo.bar.baz, are not supported.)

The second iteration is, unfortunately, less complete than I had hoped for. Its source code may be found in the compiler-js/src/main/ceylon directory of the typescript branch of the main Ceylon repository (head commit at the time of final submission: bf4248b), though that information alone is not enough to build it (see below). At the moment, it supports toplevel values and functions, classes and interfaces with attributes and methods, some primitive types (string, number, boolean, void, any), and type references (MyClass). The most important missing features are type hierarchies (in Ceylon terms: extends, satisfies) and generics; after that, many TypeScript-specific features will have to be added as well (enums, type literals like { name: string }, index and call signatures, string types, and many more). To use the second iteration, build it as outlined in the script below, and run it with the arguments ceylon run com.redhat.ceylon.compiler.js.typescript modname modversion file1.ts file2.ts …. This will convert the files file1.ts file2.ts … to a Ceylon module which is stored in modules/. You can then use this module from Ceylon just like any other Ceylon module; see above for an example – the first and second iteration are very similar in this regard. (Note that the second iteration also does not yet support module names with multiple components.)

Interop is, with either iteration, fairly straightforward: you import the module like any other Ceylon module, and then use the members. Most mappings should be obvious (functions are functions, classes are classes with a default constructor, string is String, etc.); enums map to a class with value construcors, so you can use the same EnumName.MemberName syntax as in TypeScript. Index signatures require special treatment in the first iteration (the second iteration does not support them yet, and will not require this special treatment) – you must surround your code that uses index signatures with the following lines:

dynamic { eval("Object.prototype.$_get = function(k) { return this[k]; };"); }
// your code goes here; you can use index signatures with foo[bar] syntax
dynamic { eval("delete Object.prototype.$_get;"); }

These lines temporarily enhance Object.prototype to make foo[bar] work, but the enhancement must remain temporary, since it confuses parts of the Ceylon runtime. Call signatures, on the other hand, work without such hacks. For interop examples, you may also refer to the tests of the first or second iteration.

There have also been some changes to the Ceylon JS backend compiler. The initial support for TypeScript classes (#6050), which had been added before the beginning of GSoC proper, has been replaced by a more general interpretation of the dynamic flag (#6355); dynamic values are now accessed directly instead of via a (nonexistent) getter ([6372]); and dynamic types on objects may now be narrowed (#6389). More necessary changes are outlined in #6001.

A complete setup of the second iteration may be obtained with these commands:

#!/bin/sh

# dependencies: Git, Java, Ant, Make, Node.js, NPM, Jake
# on Arch Linux:
pacman -S --needed --noconfirm git jdk8-openjdk apache-ant make nodejs npm jake

cd /tmp # or wherever

# get Ceylon distribution
git clone https://github.com/ceylon/ceylon.git
git clone https://github.com/ceylon/ceylon-sdk.git
cd ceylon
git checkout master-for-typescript
ant dist
cd ..
cd ceylon-sdk
ant compile publish-quick
cd ..

# get CTSL (first iteration)
git clone https://github.com/lucaswerkmeister/ctsl.git
git clone https://github.com/Microsoft/TypeScript.git
git clone https://github.com/DefinitelyTyped/DefinitelyTyped.git
git -C TypeScript checkout 80db0f2f166d0a3547319fce789c31604e8fc83b
cd TypeScript
npm install
cd ..
mkdir -p ctsl/modules/tsc/1.0.0
make -C ctsl install

# get JS/1.2.3 version of ceylon-crypto
git clone https://github.com/lucaswerkmeister/ceylon-crypto.git
cd ceylon-crypto
../ceylon/dist/dist/bin/ceylon compile-js --out=+USER # ignore the errors
../ceylon/dist/dist/bin/ceylon compile-js --out=+USER de.dlkw.ccrypto.impl
../ceylon/dist/dist/bin/ceylon compile-js --out=+USER de.dlkw.ccrypto.svc
cd ..

# set up working trees
cd ceylon
git checkout typescript
git checkout -
git worktree add ../ceylon-for-typescript typescript
cd ..
ln -s ceylon-for-typescript/compiler-js typescript

# run test
cd typescript
./test-typescript

If the last line of output is Note: Created module typescript/1, then everything was built correctly and the tests ran without error. (Note: it appears that simply pasting the entire script into a shell sometimes cancels after the Ceylon ant dist build; you may have to copy all lines below that and paste them into the shell again.)

@FroMage
Copy link

FroMage commented Aug 22, 2016

Thanks for the writeup, but I'd like a little bit more information about what is supported and what is not in both iterations, and how we can use a typescript module from Ceylon in both cases. Mapping examples and interop samples would also be useful.

@FroMage
Copy link

FroMage commented Aug 22, 2016

OK it's better, but I didn't see how we run the first iteration?

@lucaswerkmeister
Copy link
Author

but by reverting commit 60b4c48 and changing constants at the top of ctsl.ts and MakeFile, it should be possible to convert other modules with make modules/modname/modver/modname-modver.js.

@FroMage
Copy link

FroMage commented Aug 22, 2016

Well, but where does it find the corresponding typescript files to convert?

@chochos
Copy link

chochos commented Aug 22, 2016

right, we mean a concrete example with an actual typescript module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment