A couple years ago for a programming languages course, we wrote a bytecode compiler and interpreter for a JavaScript-like language we were using in the class (objects, prototype-based inheritance, higher-order functions, etc), and we initially started building it in Go, but the biggest thing that made us switch to C++ at that time was the fact that Go didn't have a straightforward union type.
It looks like this interpreter is using tagged unions for values, and using the empty interface to emulate a union type. I seem to remember that we may have read something at the time that recommended using the empty interface instead of unions, though I don't remember for sure. Nice to see some interpretation efforts finally being realized in Go!
It's less convenient than writing a compiler in Haskell, but then, what isn't? It does give you reasonable type safety, though. (Again, don't say that where a Haskell programmer is listening, but it's at least decent.)
Interpreters typically use polymorphic locations to hold values for the interpreted language. Technically almost any type of polymorphism is enough, but the fewer indirections you can get away with, the faster you can make a simple interpreter. So that would usually mean a sum type in a functional programming language with algebraic data types, or a simple object in an OO language, or a tagged union in C and Pascal.
But for better performance, approaches like tagged pointers (often used in Lisp implementations) are useful, as it eliminates a level of indirection for most integer operations. If you can afford to use 64 bits for your values, you might consider using doubles everywhere, with invalid exponents for stuffing a shortened pointer inside the float (I believe luajit uses, or used to use, this technique; it would probably be a good match for JS as well, as JS doesn't have integers).
It allows you to create a single, compact structure that can then be utilized in a number of ways without having to re-cast it, replace it, or otherwise reallocate it.
For example, you can have a union between a 64-bit pointer and a 32-bit type identifier plus 32-bit value. This means you can store 32-bit integers in the same space as a pointer.
As I answered above to another commenter with a similar question, JavaScript is a dynamically typed language and representing dynamic values in a statically typed language requires a bit of thinking, and unions are a common way of doing this.
The alternative is a data structure which has N-1 empty, yet allocated, slots (N is the number of possible types.) instead you can have one field indicating the type of the variable and then your code can switch which field it accedes based off that indication. Total size is one int plus size of largest type represented.
You could potentially also have an interface that defined methods for all the basic types `interface { AsString() (string, error), etc. }` and then each basic type implements this interface and returns an error if it can't/shouldn't be represented as the requested type. A type-switch could give you the same info you get from the tag and you don't pay for type-assertions
No, for once, Go doesn't have casting, it has type conversions, but they are type safe. With void * you can do anything, with interface{} you can only use the dynamic types inside the interface.
That being said, using interface{} for unions is extremely unfortunate. The bright side is that after 4 years of using Go almost exclusively, I only had to abuse interface{} only once, with compiler parsers (like the parent says). Every other time there was a better design which did not require using interface{}.
Its actually really easy to get involved in hacking on GHC, the main Haskell compiler. Seriously, its as easy as taking some time to just try to build current head and report any build problems! https://ghc.haskell.org/trac/ghc/wiki/Building
I'm actually really excited by all the people starting to jump in and try to learn/help. We've even got 2 really smart high schoolers doing some amazing contributions to GHC recently.
seriously: its easy to get involved in hacking on interesting open source projects. Just choose one you care about and stay excited by, and dig in!
I could be wrong, but I think they're just poking fun at all the "An X interpreter written in Y" where X and Y are the current trendy languages. Usually one of X or Y are JavaScript or Go, so seeing both in the same headline is like some sort of Hacker News headline jackpot.
I'm subscribed to HN's RSS feed using a feed reader app on my phone, and when I saw this item the title was truncated at around "written." Somehow I just knew it would be Go.
I entertained the idea that maybe it would have been written in JavaScript, but I think a JS interpreter written in JS has already been done at least once.
Thank you for being one of the few gophers that put newlines between standard, third party, and local imports. I wish gofmt forced that standard on people.
Note that gofmt sorts within groups. So if you leave spaces between groups of imports, it'll sort them all separately. Presumably, this sort of grouping is the reason it does it this way.
It's possible that if you have packages that understand flags (config package, with flags in init(), for example) that if you include it after the testing package, or before. One of them won't accept flags. (because flag.Parse() has already been called)
You can't rely on the order of imports initialization anyway. See http://golang.org/ref/spec#Program_execution .. I believe that's also the reason, you shouldn't call `flag.Parse()` in `init()`
Alright, 90 points, most comments being meta about the title so I'll be the brave one and ask: What is this actually good for?
I can't think of any reasonable use case. Grab little NPM ditties and incorporate them into your Go binary - Javascript to Go becomes as Lua is to C? Somebody enlighten me.
Edit: Not that this needs a use case per say, just that the intent behind it is underspecified enough for me to wonder about it.
We have distributed crawler written in go configurable with xpath and javascript for advanced crawling using javascriptcore-go[1] and also our knowledge engine serves various form of formatted information card scripted in javascript.
Duckduckgo goodies[2] also a good example of what we've been doing.
In the case of web application, you can have the same templating engine running in the browser and in the server. This means rendering the full HTML at the server level if needed or part of it or just at the browser level. You get a bit of freedom. It is painful to manage two different templating engines between the server and browser sides.
I don't think he's saying that. I think he's saying your Go server could execute similar JavaScript as the client executes.
Imagine a highly dynamic web page, beyond the initial page load when you interact with the page javascript executes the user's actions then rewrites large chunks of the page. As a developer, you need to write code on your backend that knows how to render the initial HTML for the page, but then you have to duplicate this functionality in JavaScript since the client needs to be able to render any chunk of the page that changes in response to a request.
You have a few options as a developer here. You can live with maintaining two code paths in different languages that do essentially the same thing. Or you can get rid of the backend rendering entirely, making your page less friendly to no-script users and web crawlers (and sometimes making the site flicker a bit as content gets loaded initially for all users.)
Or you can use shared code on the backend and the client for rendering HTML by having a backend that speaks JavaScript.
I distribute a binary that can be extended via Javascript/Lua/... API by a customer. This is great. I don't want the customer to mess with the Go source but I give him the chance to mess with the program by a well defined API.
Not everything needs a use case. But if you really want one, here's one: I'm a student and I want to write my own interpreter. Well, here's a good example of how to write your interpreter.
We use it because we have user programmable callbacks, and JavaScript provides a well-known sandboxed language. Otto makes it really easy to expose native functionality to JS.
Reminds me of the early 2000s argument "why do we need another browser? IE is good enough".
This project may not be the fastest JS engine or the one with the most features right now, but if nothing else, it's a really nice project for learning Go and writing interpreters.
Maybe some new ideas will be explored in this engine first, because it's faster to implement them in Go than in the (huge) V8 or other JS engines.
Having new alternatives to established software is always a good thing.
I have no idea how to really ask this, but does it uses continuation-passing style [0] to execute expressions? I tried to search for "cps" or "continuation" in the repo, but no luck. I also don't really have the time right now to go dig through the source.
Most languages don't have reliable enough tail call elimination to implement CPS without trampoline techniques to get rid of the excess stack frames, which in turn has a fairly hefty performance impact unless you're using it for something high-level like async callbacks.
Can someone explain to me whats wrong with using V8 in Go? I mean what's the point in building js interpreter in Go, when you already have a better one made.
What's the point of starting anything new, when something like it already exists? Why become a doctor when there are already good doctors? Why write a book when there are already good books? Why sing a song when somebody else already sings it better?
By putting in the context the amount of money and number of one of the best programmers in the world building a V8, building a Go V8 alternative is probable not a best business choice unless you're a huge company with clear goals
Does this aim to compete with v8 implementations for high level products usable by non go developer (I think qtwebkit, here), or is it just a mean to have js integration in go for small low level scripting ?
It looks like this interpreter is using tagged unions for values, and using the empty interface to emulate a union type. I seem to remember that we may have read something at the time that recommended using the empty interface instead of unions, though I don't remember for sure. Nice to see some interpretation efforts finally being realized in Go!