Friday, August 31, 2007

Erlang JS compiler - dealing with objects

I did some more work on the Erlang based Javascript compiler proof-of-concept. Also set up a googlecode project named 'jserl', but not yet uploaded any code.
Javascript has some functional aspects, but is also object oriented. I have used message passing for modeling objects. For every object definition in Javascript a message loop gets injected into the generated Erlang code. And I also started to implement some native functions as provided by the Javascript Interpreter. So let's take a look at an example to see what currently works and how the generated Erlang code looks like:

Javascript code:

var argument_test = function (a) {
var b = a + 1;
return b;
};

var object_test = function (arg) {
var obj = { property: 'text' };
var test2 = obj.property;
return test2;
};

var native_functions_test = function (arg) {
return arg.length;
};

Compiled to a beam file this gives the following (expected and correct) results:

Eshell V5.5.5 (abort with ^G)
1> footest:argument_test(5).
6
2> footest:object_test(no_arg_dummy).
"text"
3> footest:native_functions_test("text").
4
4>

And here is how the Erlang source code (generated with erl_prettypr:format/1 from the abstract syntax tree) looks like:

-module(footest).
-compile(export_all).
argument_test(A) -> B = A + 1, B.
object_test(Arg) ->
Obj = obj_new("text"),
Test2 = case Obj of
Int when is_integer(Int) -> "not defiend (yet ?)";
Float when is_float(Float) -> "not defiend (yet ?)";
List when is_list(List) ->
case jserl:is_string(List) of
true -> "not defiend (yet ?)";
'_' -> "not defiend (yet ?)"
end;
Pid when is_pid(Pid) ->
jserl:rpc(Pid, {get, 'Property'});
'_' -> "not defiend (yet ?)"
end,
Test2.
native_functions_test(Arg) ->
case Arg of
Int when is_integer(Int) -> "not defiend (yet ?)";
Float when is_float(Float) -> "not defiend (yet ?)";
List when is_list(List) ->
case jserl:is_string(List) of
true -> length(Arg);
'_' -> "not defiend (yet ?)"
end;
Pid when is_pid(Pid) -> jserl:rpc(Pid, {get, 'Length'});
'_' -> "not defiend (yet ?)"
end.
obj(Property) ->
receive
{From, {get, 'Property'}} ->
From ! {self(), Property}, obj(Property);
{From, {set, 'Property', Val}} ->
From ! {self(), ok}, obj(Property);
{From, _Other} ->
From ! {self(), {err, no_such_member}}, obj(Property)
end.
obj_new(Property) -> spawn(fun () -> obj(Property) end).

Of course there is a lot of stuff missing, so it is currently more a pseudo script than an ECMA compliant Javascript compiler.

1 comment:

Petar Maymounkov said...

This may be totally out line, but I thought you'd appreciate this comment. Regarding the complexity of parsing JavaScript using ugly things like Yacc,lex etc. There is something called a PEG grammar which is like CFG with one minor difference (non-deterministic choices are turned into sequential choices, never mind if you don't understand this now). Point is that PEG grammars (with a little standard sugar) are sufficient to describe all languages (even C mixed up with the macro languages on top) and especially JavaScript (which is a walk in park actually). PEG grammars can be compiled in linear time (super fast). What you need to care about as a programmer is that you have to write one clean grammar when you use PEG, instead of a parser + grammar + hacks when you use Lex/yacc. And it also happens so that the PEG parsers were initially designed for functional languages, because they are very easily implementable there. If you haven't heard of PEG grammars, it is because they recently (past 10 years) have been re-discovered in Computer Science as extremely useful. You can learn everything about them here: http://www.cs.nyu.edu/~rgrimm/xtc/.The state of the art compiler XTC is written in Java. But you can still use it to write a JavaScript to Erlang compiler, easily. I would be happy to hear if you decide to pursue this, maybe I could even help. (Find my email on my homepage)