Compiling dynamic programming languages
notes.eatonphil.com
notes.eatonphil.com
I think the "0.080 total" and "0.087 total" are just from node startup time (and the variability within that), not time actually executing the function. I just ran node on an empty file and the running time was "0.087 total".
I think when doing these sorts of perf measurements it's best to make the code run for multiple seconds to better account for startup time, JIT warmup, caches, and the various other subtle factors that come into play.
Operations you see in the compiled output like `Local<Function>::Cast(global_3->Get(String::NewFromUtf8(isolate, "Boolean")));` are extraordinarily expensive, and should pretty much be avoided at all costs.
The most immediate advantage I can think of for having a project like jsc long-term is for packaging/deployment. we use zeit's pkg tool at work today but a robust/mature Javascript-to-native compiler is much more compelling.
The early python community went for C monoliths, but there was an alternative of python objects carrying C pointers. So python provides a form of api/plugin runtime dynamic linkage for smaller-grainsize C libraries. I don't know if anyone has explored that for javascript. If you've a problem domain of interest in need of almost-native speed (pointer call rather than static linkage) with 'compute-intensive runtime assemblages of C chunks' (eg, graphics or scientific), it might be a possibility.
> [...limited subset of javascript...?]
I don't quickly find the comment I intended to reply to, but fwiw, note that the ecmascript spec is written in sort-of pseudo-code English. Many years back I framed spec implementation as a semi-manual textural database cleanup and transformation exercise, and in a couple of days massaged spec into code. It helped that the target language had label gotos and permitted arbitrary identifiers, so the transformation was simple, and the resulting code looked like just like the spec. (Kind of ironic to see a comment elsewhere on this page dis'ing regexps.)
I'd recommend it as a project to any wanting to learn more about a particular language (implementing a language teaches you how it works in painstaking detail), and get a better 'feel' for how languages and compilation works in general.
name (...) as (...)
name (...) = (...)
name (...)
...where (...) represents an arbitrarily complex expression. The first one is a "name as" command, the second is an assignment to an array element, and the third is a procedure call, and you can't tell which until you've fully and accurately parsed the first arbitrarily huge expression.
Also, the following:
name:
In BASIC a colon separates two statements on the same line, and an empty statement is valid, so is this a label or a subroutine call followed by an empty statement?
Edit - just remembered this gem: You can use a "With" block to save having to type the name of an object when referring to its members...
With SomeObject
MsgBox .Name
End With...will pop up a dialog showing SomeObject.Name. Now go ahead and tokenize that second line. If you're like me you got three tokens: [MsgBox][.][Name] The problem is that's indistinguishable from...
MsgBox.Name
You could say that ".Name" should be one token. Hmm. So what if it's something like...
MsgBox .Name.Substring(1, 3).ToUpper()
Still one token? I think my hair's going to be white by the time I finish this project.
> Built in, in the sense that they're first class statements built into the language.
:O
> 94 year old hacker in the great state of Texas
Wow, I hope I'm coding at 94!
I get the same icky feeling when I look at some of the things they've shoehorned into JavaScript over the years, though, like regex.
That said, reading TFA and the linked BSDScheme one gave me some ideas for the next time I get around to playing with minischeme. Too many toys and not enough time...
I considered writing this in C++ to get more tooling (JS parser, C++ AST libraries, etc.) but in the short-term I do not see myself switching. I'd be a little more likely to switch back to D though because I find data structures in Rust annoying.
To some degree it will always be "messier". 30-60% of generated code will just be converting between C++ and V8 types.
But really, I just need to prioritize using more tmp values so I'm not shoving crazy amounts of logic in one line.
It can definitely be difficult to debug. Things would be easier if I were generating C++ ASTs and pretty-printing it (I should probably do this in the future) but for the PoC I'm just emitting strings of C++.
Chicken Scheme's generated C is more along the lines of what I'm aspiring to.
Who is normally reading this stuff?
Examples:
* For a given DP algorithm, you might evaluate it top-down (recursive memoized call) or bottom-up (filling in the table values in topological order). Top-down is easiest to implement, but bottom-up can avoid stack depth limits and I think can be more efficient if implemented well. A language specifically crafted for it might let you write the simpler recursive implementation and efficiently evaluate it bottom-up.
* Sometimes you might want to run the same DP algorithm in different contexts, e.g. operating on different data sets (where the data set is constant for a recursion tree). Keeping the data set (or even a pointer to it) in each cache entry is wasteful if your cache has 100 million things in it, and passing it down as a parameter is also not as efficient as it could be. I guess closures may solve the problem, but maybe there are smarter ways that a language could help here.
I don't want to diminish the work this guy has done, but one of the biggest challenges, unless you want to take credit for someone else's work, is to do all the various kind of optimization that a mature compiler would do, at various levels.
So yeah, that's cool, but I was expecting more given the use of the word "compiling".
A "transpiler" is therefore a specific type of compiler.
I view "transpiler" as a recent informal term to be a little more precise/honest when describing compilers targeting JS, but not one that people should feel obligated to use in place of the more generic term "compiler".
If we think about it javac would not be a compiler otherwise :-)
In the case of JSC, a binary is produced currently but it must be launched by a Node app. So it doesn't exactly meet my criteria. But being able to produce embedded V8 code will not be significantly more difficult for these examples. (Recreating Node's stdlib would of course be difficult but that's separate.) I'm hoping to have a Node-independent target soon.
You are "dependent" if you want on the RunTime (rt.jar). The way it was explained to me, and the model which I sticking to in my mind is:
Abstract Machine/language + RunTime = Level of the onions of a computer.
Assembly/CPU + libc Bytecode/JVM + rt.jar
The language manipulates the resources provided by the machine (registers, stack-machine etc etc).
My recollections are fading though - but I found this model to be good enough to explain me well how a computer works.
I don’t know D enough to know whether GC is enabled in this project, which is a major weakness in many dynamic languages that one might imagine aot compilation could eliminate.
"Transpiler", which is arguably somewhat of a buzzword, typically refers to compilers where the source and target language are either the same, or at a very similar level of abstraction; I agree with n4r9 in considering them a particular type of compiler. Even so, the implementation described in the article compiles from a high-level language, JavaScript, to a considerably lower-level language, C++; so even if we exclude transpilers from our definition of what constitutes a true compiler I still think it's a valid term to apply in this case.
1. http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf