Crafting an Interpreter in Java

Crafting Interpreters is divided into two parts: the first half uses Java to build an interpreter, while the second uses to C++ for a bytecode compiler. One of the things that drew me to the book was that, in addition to learning how to implement programming languages, you also pick up two different programming languages along the way. I am relatively inexperienced working with Java, so I am writing a few posts documenting my experience working with it here. To begin, I wanted to lay out a few general reactions I had. Follow up posts will dive into some of the tools from the Java ecosystem I used to improve my experience.

A Note on IDEs

Many languages are editor agnostic. Java is not one of them. Most people in a professional context would use a Java specific IDE to work with the language. I’ve used IntelliJ and its cousin Android Studio in the past. If you have no editor preference at all I think IntelliJ will make your life the easiest. That said, I am not using an IDE to go through this book, and I think it’s totally feasible to use any editor you want to work on a Java project this size.

Package names and Directory Structure

Introductory documentation and tutorials for language beginners often focus on the syntax of the language in question. In my experience, that is rarely where I get stuck. Almost always I run into problems when I try to make the transition beyond a toy project consisting of a single file. With Java one of the first sticking points I ran into was the directory structure. The directory structure of even small java projects always seemed intimidating to me. Actually the way Java projects are usually organized makes a lot of sense if you know what’s going on.

Designed for scale

Java is a language that’s popular at huge companies with huge codebases. New programmers may assume that these companies stick with Java simply out of habit. In fact some of the things that seem unnecessary, like Java’s clunky directory naming, are “features” not “bugs” when you are working at a larger scale.

Maven Standard Directory Layout

Maven is a one of a few competing build tools for working on Java projects. As well as providing the tools to build Java projects, the Maven project also publishes standard conventions for organizing Java projects, and a directory of published Java projects you can use as dependencies. For my project I chose to use Gradle to build my project, which also follows the Maven Standard Directory Layout. It’s worth checking out the documentation yourself, but this layout describes the very top level of the directory structure. We’ll cover the most relevant one real quick:

Directory Description
./ READEME, build files, License, etc.
src/main Your source files
src/test Your test files

Not so bad right? Gradle projects further nest this inside an “app” directory, setting you up for success if you need to add any subprojects to your repository.

Directory Description
./ READEME, License, gradle settings, subprojects, etc.
app/ build file for the app project
app/src/main Your source files
app/src/test Your test files

What’s the deal with “com”

Ok that’s all good and well, but you may remain perplexed when you open up src/main and are greeted with “com”, followed by a seemingly interminable number of useless directories before you find any actual code. There are three principles at work here.

  1. Inside src/main the directories should may directly to package names
  2. Package names should ideally be unique across all your dependencies
  3. If you always name your packages after a website name that you own, you are unlikely to conflict with other package names.

This might seem extreme to you, but consider how awful working with dependencies in environments like nodejs and python can be. This is just one way that the Java ecosystem seeks to make sure things “just work” even on enormous projects. I’ve come to appreciate the reasoning here, even though I do find all the empty directories pretty irritating. Now you’ve got something like this:

Directory Description
./ READEME, License, gradle settings, subprojects, etc.
app/ build file for the app project
app/src/main/com/goodskynet/coolproject Your source files
app/src/test/com/goodskynet/coolproject Your test files

Collections

Modern languages usually provide convenient syntax for working with lists and dictionaries. Java instead provides libraries with lots of different collection implementations, optimized for various use cases. I won’t go through all the usefull ones here, but you should expect when the book decides to create a list of object instances, you’re going to be importing something like java.util.List (to type your variables) and java.util.ArrayList (the actual class you instantiate), instead of using []. Actual quote from the book: “We must also chant a minor prayer to the Java verbosity gods since we are using ArrayList now.”

Running the code

The java executable has a ton of options for configuring its behavior. If you plan to use the executable directly, you’re going to have to learn about some of them. If you value your time and sanity, I would encourage you to let your build tools handle this for you. Build tools like Gradle and Maven provide a lot of additional benefits anyway, which I will cover in the next post.