My experience writing my own language & interpreter
As someone who leads a QA chapter, I obviously still code, just not all day like when I was still a developer or SDET. To scratch this itch, I work on my own projects in my free time. Creating games, helping friends and family automate repetitive tasks at work or in their lives, creating tools for myself, etc. Usually this works just fine, just not this particular day. One of my problems is that my creativity comes in waves. I can have ten different ideas at the same time and then, on other days, I completely lack inspiration for what to build. This was one of those days.
I had been low on inspiration for a side project for a while and at the same time I felt oddly nostalgic for using Java again. It is the language I 'grew up with' and you don't use it much anymore these days with most work, QA or Unity/Godot projects. I also felt more like using a "pure" language instead of having to fiddle with UI at that time.
Then I overheard my wife complain about a certain language or framework that shall not be named. It just didn't read nicely and it clearly wasn't her favourite. That gave me an idea. I had just rewatched a talk about Rockstar, the esolang where your program is also a rock song. Both ridiculous and amazing at the same time. Ever since I was a kid, I always wanted to write my own programming language. So there it was, I was going to write my own language. What remained was picking a theme and what to pick was clear. It had to be something my wife really cares about and something she could hopefully appreciate. So that just had to be... dogs!
Disclaimer: Bark is nothing more than a hobby project: Not useful, not for work and I most definitely didn't always make the best choices. Some on purpose because it felt more fun, sometimes because I am not an expert on this topic. It was to learn and to have fun. If someone smiles while running a .woof file, even briefly, that counts as a massive success to me.
If you are curious:
- GitHub
- Documentation: See Tools/bark on this website
- PyPI
- Maven Central
What Bark actually is
Bark is an esoteric programming language (esolang) dressed up as dog-themed stories. Where normal languages optimise for speed or specific tasks, esolangs optimise for a joke, a puzzle or a certain concept.
For example, Rockstar (together with Brainfuck, my favourite) asks what would happen if code were a rock ballad. Bark asks what would happen if code was something that is close to your heart (dogs) and makes you feel happy instead of frustrated by syntax that could bore you like Dutch tax forms.
You write .woof files. An interpreter runs them line by line. No compile step and no .class output generated from your story.
Now obviously, as I mentioned, any esolang needs a gimmick. Something that makes it feel unique. So I thought about how dogs listen to their owners. It should read like stories, as dogs have nothing to do with curly brackets, formulas, brackets, etc.
A dog also doesn't hear:
"Listen up Bimba, I will finish my coffee and then we will go for a short walk. But only short today as it rains, okay?"
They hear:
"Bla bla Bimba, bla bla bla bla walk bla bla"
They react to their name and the word "walk". So that is how my language behaves as well. Where most languages have lists of allowed keywords and predefined lists of allowed glue words, Bark just ignores whatever it doesn't recognise. No separate ignore lists. If it isn't registered, not a keyword and not a value, Bark simply pretends it didn't hear it.
The funny bits (or at least why this language is not normal)
What makes Bark unique? A few highlights. Not exhaustive of course, but at least quite a few of them:
- The runtime only reacts to words it knows. The rest is considered noise and gets ignored.
.wooffiles are short stories. Plot is optional and commands hide inside sentences.- Thanks to allowing glue words, the language can be written in story mode (spaces, filler words, pronouns) or script mode (underscore names, no filler, good for tests).
- Story-only lines with zero recognised words do nothing unless
--strictis enabled. - Variables are dog breeds from a predefined list. You cannot invent your own variable names like
my_var. - Objects, stashes (lists) and piles (queues) also come from registry files (world objects, toys, etc.) to keep everything dog themed.
- Pet names and pronouns can be used for variables. Example:
her name is Bimbaafter defining alabrador. Later,Bimba shares with the dachshundresolves to the labrador, and so doher,his, etc. when referring to the last mentioned dog. - Only two program-wide globals exist:
memory(number) andjournal(string). - Nine dog fields per breed (age, name, toys, treats, inventory, etc.). Story words are aliases:
toysare items,treatsare food, etc. - Default traits per breed. For example, a dog can be
loud. That forces every print to be uppercase. You can override those settings by saying something likemy corgi is loud. Some traits can really throw you off. Some dogs are so greedy that subtracting a number subtracts one more. - Assignments are very flexible.
she is 2sets the age, for example. No attribute name required. - Many print verbs, each with different formatting.
growlprints in uppercase,yapprints twice, etc. You also have dog-voiced prints affected by behaviour (she woofs) versus narrator prints (I bark) that are never affected. - Dog-themed errors. More on this later, as this one kind of backfired!
- Easter eggs like banners and hidden prints.
For the full list, please check the documentation.
Example app
What does a program even look like? To give you a short example:
I have a labdrador
She is 2
She has 3 toys
Her name is "Bimba"
she woofs how many toys she has
A slightly more complex example:
labrador
my labrador has 3 toys
when she has more than 2 toys then
she woofs "Plenty"
she misplaces a toy
otherwise when she has 1 toy then
she woofs "Just one"
otherwise
she woofs "None"
bury
while she has more than 0 toys then
she woofs "Still going on"
she misplaces a toy
bury
You can try these out in the try-it area of this website.
Seeing a .woof file execute for the first time was probably the moment I got most excited about the whole project.
Compiler, interpreter, transpiler and what I picked
If you don't really care what happens behind the scenes, you can skip the next two chapters.
When I said that I created a new programming language, what did that actually mean? Well, when you write a new language, which is nothing more than your own written-down syntax, something still has to run it. For Bark that something is written in Java (yes, I am aware that is a terrible choice for this!) and later Python so I could run it on my website.
That translator can be written as a compiler, interpreter or transpiler:
- Compiler: turns a program into another form before it runs (machine code, bytecode, .class files, etc.).
- Interpreter: reads the program and runs it step by step. No separate output file from the source.
- Transpiler: converts the language into another one (like Bark to JavaScript) and then the output language runs.
For a small hobby language without serious intentions, an interpreter is usually the most practical choice. Fast to build, easy to change and good enough because nobody cares about speed or efficiency anyway.
I picked an interpreter twice: JBark (Java) and pBark (Python).
Lexer, parser and interpreter (quick bite)
If you have never built a language before, it is good to know it mostly consists of three parts and there isn't a lot of magic involved. You write dog stories. The interpreter scans for words it knows.
The three parts, only briefly explained as I am not an expert on the topic and because you are probably not that interested in this part:
Lexer
The lexer chops a line into tokens like words, numbers, quoted strings, etc. It doesn't care what those things actually represent.
Bark, like most esolangs, cheats a little bit. A classic lexer usually just collects tokens. Mine mostly follows that idea but also does a few small chores along the way like punctuation handling and a bit of filtering.
Example:
she has 3 toys
becomes:
she, has, 3, toys
Parser
The parser turns tokens into a small plan. What do we actually need to do with that line?
Example:
SetAttribute(she, items, 3)
Or, to use a slightly more complicated example:
she woofs memory minus how many toys she has
becomes:
Print(WOOF, [Binary(MINUS, memory, Field(she, items))])
Can you already see I kind of went overboard a bit?
Interpreter
The interpreter runs each line's plan.
For example:
she woofs memory minus how many toys she has
would make the interpreter perform a print action that prints the current value of memory minus the toy count of the dog referenced by she.
Why Java again? And Python?
I wanted to use Java again for something. hat was honestly the main reason. Java 25, Gradle, the whole thing.
For a hobby esolang, a high-level language is more than enough.
Why Python? Not because I wanted to or thought it needed one. It only got added because using Java in a try-it section on my website was a lot harder than I anticipated and Python was much easier to integrate with a few libraries. Same behaviour as it was auto-translated from the Java source, but it didn't get quite the same love as the Java version.
I will admit that I secretly feel a bit proud that this joke actually ships for real. GitHub releases with a fat JAR and launcher scripts. Maven Central if you want it as a library and PyPI so you can install it with pip install pbark and so the website can display the language.
The dogs (the real ones)
Bark is of course named after barking. The examples use dog names or real dogs. Three of them in particular. Let me introduce you to them.

Bimba She is suspiciously clever (and severely underestimated by my wife's family). Food-motivated in a way where you are barely safe holding a fork at the dinner table, loves belly rubs and is just a gentle, slightly overweight (sorry Bimba) round ball of fluff.
Pepon is my wife's dachshund mixed with... who knows?
We don't know, as both Pepon and Bimba are rescue dogs saved from the streets.
Small, stinky, simple and sweet.
We aren't sure if much is going on behind his charming eyes, but we do know there is only room for love and affection. A bit of a stalker as well, so be prepared to be followed everywhere.
Hazel is a corgi puppy.
She belongs to mutual friends. We met her after offering to dog-sit her.
Cheeky, really intelligent and slightly jealous when she is not the centre of attention. She stole my heart by sleeping on my feet while I was working and by trying to steal my shoelaces so I couldn't leave.
I never really liked corgis, but she solved that problem.
The parts I really enjoyed
The lexer and early to mid-finished parser were a lot of fun.
Splitting text into tokens felt really satisfying. Wiring lines together (registering breeds, assigning numbers, printing strings, etc.) was much easier than I had anticipated and I was able to write it faster than I expected. I must have had quite a smug look on my face after the first few evenings.
The "dog only hears certain words" rule matched the joke theme quite well and kept the early parser fairly small too. Smaller than it would have been with a huge keyword list.
As a QA/developer and not a compiler academic, this was a great way to get introduced to the topic.
It was also working almost immediately, which surprised me in a good way.
Where I kept going and probably should have stopped sooner
When everything is going so smoothly, it is really tempting to keep adding more and more complicated features. Especially when you keep thinking of new funny things to add.
With some of the features added, the joke kind of turned into unpaid overtime.
Loops
Did I really need while, until and for each loops? Are loops needed at all for such a simple language? And if so, did I really have to add three different loop types?
As this was the point where I still had lots of motivation, I pushed through and managed to get them all in.
The difficulty here was that loops had to cooperate nicely with story glue on the same line and only really made sense if they supported multi-line statements, while the language originally started as single-line commands.
Flexible assignments
This one still makes sense to me. I wanted stories to feel loose and natural. But they quickly added some complications.
To be able to handle pronouns, assignments without attributes (she is 2), extra descriptive words (she is 2 years old), spacing differences (toy box versus toy_box), multiple dogs on the same line and flexible word order turned the AssignParser into a file hundreds of lines long.
Something I would normally never approve of at work as someone who cares about clean code.
Tricks
Functions, except they are tricks and the syntax is story-shaped. Parameters, bodies, return values, calls with with, etc.
Initially they clashed with other commands and it became hard to keep understanding what I was even writing and how it should behave.
In hindsight, Bark doesn't really need functions. If I had to do it again, I would probably leave them out entirely.
Comparisons
The devils.
Comparisons were where I almost lost motivation for a while. I never expected this part to be so complicated.
Natural language comparisons like: she sniffs less toys than 8 sound great until you have to write the ConditionParser for them. It is huge. It was tricky to make word order feel natural, avoid clashes with other parsers and still handle all the edge cases.
I also decided to rewrite most of the parser when I reached comparisons because I simply wasn't going to manage with the original approach. Even now, when I read those files, I am happy I left so many comments behind or I would have no idea what I was doing.
Did Bark need comparisons?
Probably.
Could they have been simpler?
Probably yes as well.
What I learned
I expected the AST structure and interpreter to be the hardest parts. They weren't. The lexer and interpreter are surprisingly straightforward once you understand what they need to do. Even the basic parser tasks were less intimidating than I expected. The comparison and loop parsers definitely kept me awake a few times after midnight though.
I should also remember to finish things properly before adding more features on top next time. Ignoring that lesson forced me to rewrite most of the parser halfway through the project. A painful reminder of why I normally build things in small iterations and keep them simple first.
Esolangs give you permission to stop
Nobody is waiting for a specific feature set or a Bark 2.0 release. You can simply decide it is done and move on. No need to be too perfectionistic.
Dog-themed errors are funny until you are debugging late at night and you get: no scent of "X" anywhere. Very helpful, thank you...
Bark also occasionally reminded me that it has dog behaviour. I spent far too much time debugging additions and subtractions and wondering why something returned 2 instead of 3 before remembering that the dog I was using happened to be greedy. Quite a few of my bugs turned out not to be bugs at all.
If you are curious about interpreters and writing your own language, the topic is actually very approachable. You don't need a PhD in linguistics or mathematics. You need a few free evenings and a certain amount of stubbornness (no lack of that here).
And ideally, you should be smarter than I was and not overcomplicate things.
If I built Bark again
Looking back, there are a few things I would probably do differently:
- Keep comparisons simpler.
- Decide earlier whether multi-line constructs really belong in the language.
- Be stricter about finishing parser work before adding more features.
- Skip features (tricks) entirely unless I had a really good reason.
- Spend more time thinking about error messages and slightly less time trying to make them funny (ideally combined).
Then again, some of the most frustrating parts also ended up being the most educational. So maybe I would still make a few of the same mistakes.
Worth it?
Absolutely!
It was a great project that didn't just allow me to use Java again, but also sharpened some slightly dulled programming muscles as it was more challenging than writing test frameworks, scripts or the occasional game.
I also finally built a programming language, even if it is only a joke language.
Most importantly, my wife now has a language that she might actually enjoy reading.
*Alex Hovenkamp
