Tuesday, October 28, 2014

Thoughts on the First Steps of Wake's JS Interop

For the last several months I have been creating the language Wake, and now with hard work and help from friends its nearing feature completion as a usable language. However, there's a huge goal of the project that has yet to be realized -- compiling to multiple languages, and having smooth interoperability with them.

At the moment wake compiles to javascript, so that's where our interop features will begin. My original plan for Wake's js interop was to create a whole new compiler from scratch, written in wake, that compiled a file with a custom syntax, and spat out ordinary wake compiler artifacts:

wake-js-interop JQuery.wkjs -d bin/waketable -o bin/wakeobj/JQuery.o
creates files: bin/waktable/JQuery.table, bin/wakeobj/JQuery.o

There does have to be some "glue" code that sits between Wake world and real JQuery, so JQuery.o is the glue and JQuery.table describes its usage to the Wake compiler. JQuery.wkjs would usually look like simple wake, but other things would be very custom syntax.

every JQuery is:
    Object[] -- map(Object[] what, Object fn(Object) mapper) = "$.map(what, mapper)"
    JQElements -- find(Text selector) = "return $(selector)";

What are the problems with this approach? Well you take into account all of the semantics and file encoders and everything else that has already been written in the wake compiler, from inheritance to interfaces to providers to circular dependency relationships, its a ton of work to do. Meanwhile, the first step would be to code a clone of Yacc in wake which is its own difficulty. So last night I took a different approach and its a solid proof of concept! Simply create an abstract class in Wake

every Console is:
    log(Text);

compile its table file (usage definitions) only:

wake Console.wk -d bin/waketable -t

and use the 'wake-js-gen' github project on the table file (in this case the last argument is saying that we're wrapping the singleton var named "console" but there are other options such as wrapping constructors)

wake-js-gen bin/waketable Console bin/wakeobj/Console.o console

and now you can console.log in js from wake!

Console.log('test!');
//test!

In the immediate moment, I'll probably use this to create some simple wrappers for JQuery, Regexp, etc. so that we can code against them ASAP. But next, we have to come up with a strategy for making this more flexible. But I'm thinking about bringing in annotations for the task:

extern JQuery is:
// 'extern' instead of 'every' will tell the compiler not to create a .o file
// and that these abstract methods aren't abstract

    @Js("return $(selector)")
    JQElements -- find(Text selector);

    @JsSingletonMethodOn("$")
    Object[] -- map(Object[], Object fn(Object));

This way your wake source code can be the master copy, and the tools can still be codegen-only, no validation or parsing. Not only does it make my job easier for the js generator, but it makes my job easier for every other language we support from here on, and it makes it easier for others to come by and create these generators themselves. What do you think? Is this a good interop strategy or a bad one?

No comments:

Post a Comment