Crafting Interpreters - Reflections 2018-10-31
I mentioned earlier, I think, that I'm going through Crafting Interpreters, but I'm too much of a prima donna to touch Java, so I've been converting from Java to Lua as needed. (I will probably convert the other interpreter to Lua again, Python 3, RPython, or Rust, because C terrifies me.) To start off this week, what's that been like?
Kind of a mixed bag, but all of the things being mixed together are basically fine. Lua's treatment of different types of value is more uniform than Java's, and Lua's value semantics line up well with Lox's, to the point that the is_truthy function is just the identity currently. On the other hand, Lua is deliberately agnostic about a number of design decisions that object-oriented languages tend to make, such as:
- What are types?
- How does inheritance work?
- Is this a sequence or a hashmap?
- Must exceptions be of some type?
As it happens, I can get away dodging most of these questions; the code so far hasn't really relied on Java's implementation of particular language features in a detailed way. Let's take a look at what I did have to hand-roll.
One of the more obvious divergences, to me, is that, because imports are at runtime, cycles are Pretty Bad. (Python can sometimes handle import cycles better, but not always, and I don't know how to tell when it's going to go badly besides "It never seems to work right for me.") As a result, instead of having things import my lox module, I have it construct a table, which the various parser objects require to be passed into their constructor. Missed out on a golden opportunity to call it the LoxRuntimeDependencyInjector (... FacadeFactoryProxyAbstractSingletonVisitorBean[#]) or something. Anyway, this means that I don't get to treat the class as a singleton by writing a bunch of static methods, since most of those concepts don't map cleanly onto Lua anyway.
My implementation of the visitor pattern might look a bit off. Basically, instead of keying off of classes (which don't exist), I have visitable objects provide an accept method that indexes the visitor with the object's constructor, to obtain a concrete visitor function. Now, I couldn't decide whether to make the visitor function take the visitor as a first argument, so I made a "dynamic" version that does, and a "static" version that doesn't. To simplify things, the visit method handles the calling convention, and the accept method just retrieves the function.
Other things going on:
- The implementation of enums is a one-off for token types.
- I have a module that's just some utility functions for creating and manipulating hashsets.
- The current "object" module is tiny, and has no dependencies. We'll see if that keeps up.
- I wrote some "exception" stuff so I'd have something fancier than strings for my calls to error, and a wrapper around pcall to provide something like try-catch semantics.
Where I need to go next is to look over chapter 7, and figure out how I want to finish implementing the various operators. I had actually forgotten the differences that arose from not having a switch statement, but those are there, too.
I'll try to explain in detail why this is bringing me up short, next time.
..[#] I may as well note that this is unfair of me, since I've never used Spring and probably have no reason to care what it names its classes.