Sometimes, you just have to create a programming language

check_circle Completed |

Goals

The goal of this project was to create a compiler that generates JVM bytecode. My criteria for success were simple: implement variable declarations, condition statements, loops, function definitions, and function calls. Anything else would make this project stretch longer than I want.

The repository is on my GitHub profile: https://github.com/IfeSunmola/earth-lang

Initially, I tried to generate C code, but it wasn’t as straightforward as I thought.


Sample Code

See the samples directory in the repository for more examples.

fn add(a: int, b: float) float {
   // yeet is equivalent to `return`
   yeet a + b 
}

fn sayHello(name: str) {
    var greeting: str = "Hello " + name
    _ = yapln(greeting)
}

fn main() {
   // All expressions MUST be attached to a statement. Since `yapln` returns
   // `nada`, we can attach it to an unnamed statement with _.
   // Below is equivalent to `var res: nada = sayHello("Cassi")`
   _ = sayHello("Cassi")
   
   var sum: float = add(1, 2.0)
   // To keep it simple, yapln only accepts strings, so we must convert.
   _ = yapln(floatToStr(sum))
}

The title of this article doesn’t really give justice to how much of an obsession creating a compiler/programming language was. Sometimes, I gave up on working on it before I even started. Don’t believe me? Look at the following screenshot from my GitHub profile:

Previous attempts at a compiler 2

I’m embarrassed to say that’s not all:

Previous attempts at a compiler 2
Previous attempts at a compiler 2

Do us both a favour and don’t ask about the names I chose 🤝

Why did I do this?

It seemed like a fun thing to do. Sure, there were times when I wanted to smash my PC and fight with my brain because I couldn’t figure out why code generation wasn’t working for something, but it was fun (Stockholm syndrome?).

Why did I keep “failing”?

To clarify, my definition of “failing” is if I come back to the project like a week later, and the code seems foreign to me, or I don’t understand why I did something a certain way.

  1. I was trying to do too much with little knowledge, which eventually led to being overwhelmed/frustrated.

  2. I was trying to force Java into a language that it’s not.

    • I blame my mini Haskell obsession for this. There wasn’t one specific thing that made me “fail.” It was the little things such as:
      • All methods must return something,
      • Avoiding exceptions
      • Keeping methods pure, e.t.c.
    • Java has added a lot of functional features over the years, but it still remains an object-oriented language where mutability is easier than immutability, and exceptions are the norm. Trying to break away from that in your entire codebase is just begging for future headache.
  3. I apparently don’t understand C as much as I thought? I initially started by trying to generate C, but I kept hitting roadblocks. My first attempt at JVM byte code generation was successful, so maybe the universe is trying to tell me something.

  4. I spent too much time trying to come up with the “best solution” for everything, and when I eventually found the “best solution”, I got confused and forgot why I was even trying to solve the problem. This would be somewhat justified if I’m creating a library or software for real-world use, but I’m not. (Sidenote: The “best solution” was, in fact, not the best)


Although earth-lang succeeded based on my definition, I probably won’t understand much of it in a few months, especially the code generation part. The class file API was designed in a way that forced me to rely on references to the current builder object. It works, but it felt hacky. I’m sure there’s a better way to go about it, but I couldn’t figure it out and did not want to spend too much time on it.