rainbow-js looks very cool. Excellent work!
Is it possible to use rainbow-js outside of the browser? What other uses are there for it or other examples?
I wrote Rainbow.js with the idea that it could be hooked up to whatever external definitions of System_in, System_out, and System_err are provided to it.
In fact, the file rainbow.js itself is laid out as though it's in function scope, rather than just global scope. It declares all its variables, except for System_in, System_out, and System_err. For instance, I believe you could even do something like this...
; Untested code!
(w/outfile out "rainbow-constructor.js"
(w/stdout out
(pr "function makeRainbow( System_in, System_out, System_err ) {"
(filechars "rainbow.js")
"}")))
...and get yourself a new library that can make multiple instances of Rainbow. (Keep in mind that this Arc code can't be run in Rainbow.js itself, thanks to the file IO involved.)
Alternatively, you can do this...
; Untested code!
(w/outfile out "rainbow-constructor.js"
(w/stdout out
(pr "exports.makeRainbow = function (
System_in, System_out, System_err ) {"
(filechars "rainbow.js")
"};")))
...and now you have a Node.js module with the ability to construct Rainbow instances. Implementing the streams in terms of Node.js streams is an obvious choice, but not the only one, and maybe not even the best one. :)
I recently found out that ES.next is supposed to change ECMAScript so that there's no global scope; it's lexical scope all the way down. Rainbow.js should cope with that change pretty well (which is NOT to say it will take advantage of new optimization features or be secure for mashups).
This "stream implementation as a parameter" approach could also generalize to apply to filesystem operations. That is to say, there could someday be a "filesystem" parameter in addition to "System_out" and friends, and Rainbow.js could define file operations in terms of it, just in case someone had a good idea of how the filesystem should behave. Like, the Rainbow.js filesystem could delegate to Node.js's filesystem operations, or in the browser, it could provide an awkward interface for local storage or AJAX requests. That's not in the current Rainbow.js, but feel free to fork it or something. ^_^
I just updated the REPL (http://rocketnia.github.com/rainbow-js/test/) so that the global variable 'window is defined. It's as easy as putting this code after Rainbow.js, but in the same scope:
Symbol.make( "window" ).setValue( new JsObject( window ) );
Well... it was as easy as that after fixing a bunch of JsObject-related bugs. :-p
Using this, it should be generally possible to get whatever else you need using eval: (java-invoke window 'eval (list "2 + 2")). A more in-depth way to extend Rainbow.js is to define new builtins just like the others are defined:
// Untested code!
function Alert() {
Builtin.call( this );
// Finish Builtin initialization, which sets the named symbol to
// this function instance.
this.init( "alert" );
}
Alert.prototype = new Builtin();
Alert.prototype.className = "Alert"; // for error messages
// A specialized one-argument implementation, for speed. (Well, speed
// in Java Rainbow at least. I haven't benchmarked how effective this
// still is in Rainbow.js.)
Alert.prototype.invokef1 = function ( vm, arg ) {
alert( "" + arg );
vm.pushA( ArcObject.NIL ); // the return value
};
Alert.prototype.invoke = function ( vm, args ) {
Builtin.checkExactArgsCount( args, 1, this.className );
this.invokef1( vm, args.car() );
};
// Now invoke that initialization process. The standard builtins are
// initialized in Environment.init(), but that's just to be consistent
// with the Java code.
new Alert();
This may be especially necessary if you want to define IO operations. Rainbow.js defines 'readc, 'readb, and 'sread so that they're synchronous to Rainbow.js code (just like in Rainbow) but implemented in terms of asynchronous JavaScript code. Making them asynchronous operations would also be somewhat tough, since you'd have to start a new Rainbow.js continuation for handling the event.
Starting a new continuation isn't all that bad. Rainbow.js doesn't define a standard mechanism for it yet, but you can currently take advantage of its REPL implementation:
var result;
Console.compileAndEvalAsync_( new VM(),
ArcParser.readFirstObjectFromString(
"((fn a a) ((fn a a) 4 3 2) 1)" ),
function ( error, r ) { result = r; }, !!"sync" );
The !!"sync" parameter means that it will not allow asynchronous operations. If 'readc, 'readb, and 'sread can't finish their read operation synchronously, an Arc exception will be thrown.
Anyway, I hope this is the kind of information you're looking for. I'm open to any questions and suggestions. ^_^