Introducing UnoScript
I imagine it’s a fairly common item on a programmer’s bucket-list: design a new programming language. For people that spend all day working with languages of other people’s design, there’s a romantic appeal to building a language of your own, a chance to build something abstract yet capable of improving your day-to-day experience.
UnoScript is not that language.
At the start of 2020, I finished a several month project to design and implement my own programming language. This project began with buying a book on Lex and Yacc and brainstorming what new language I could build. I decided to take inspiration from the UNO card game and see where it would go. I had grand visions: a strongly-typed languaged with UNO colors as types, with control flow inspired by UNO action cards, full I/O constructs, the works. A couple months in I realized I was just building a clone of BASIC.
Choosing better contraints
My interest in the project plummeted immediately. I had wanted to build something new, not an even-more-basic BASIC! Looking back on my design process, I realized that by following examples from my Lex/Yacc book, I had followed the authors’ design choices by default, making the original inspiration an afterthought. So, I went back to my notebook and started thinking about the language UNO-first. What renewed my interest in the project was an intriguing constraint: how useable could a language be, when not merely inspired by UNO but implemented in UNO?
From this perspective, there’s an existing alphabet (set of symbols in the language) given by the cards in an UNO deck. It’s the designer’s responsibility to build a useable language out of these primitives.
This constraint made several things clear.
-
First, a stack would need to be central to the language. This translates the mechanics of an actual card game, with draw and discard piles, into native language constructs.
-
Second, the language would need to be more procedural and less structured. Thinking of a game of UNO, where a series of cards are played one-at-a-time, translates to a language where nested scopes and a local/global distinction feel foreign.
What emerged was UnoScript, a language somewhere between Forth and a Turing machine.
Writing an interpreter
I implemented the interpreter in C++, which meshed well with the output of Lex. Interestingly, since the language has no concept of scope, parsing by Yacc/Bison was uneeded.
Looking back on my design choices, there’s a few things I would reconsider:
-
By default, I started the project from an object-oriented perspective. This let me model the language resources well by writing wrappers for
std::deque
andstd::vector
. However, integrating these objects into the main program loop felt clumsy, as I noticed myself using lots of copy-constructors and few references. It’s unclear who has responsibility for the card structs being passed around. -
Instead of choosing an object-oriented language to implement a procedural language, a scripting language such as Python may have been the better choice.
-
On the other hand, I think there’s a strong argument for keeping C++, which works well with Lex and has a performant standard library. I’m also curious if allowing the use of C++’s more procedural constructs (i.e. the dreaded
goto
) would allow the interpreter to more closely match the target domain.
Final thoughts
Writing UnoScript was a rewarding experience. Producing an unstructured language saved headaches sorting through parsing and building a syntax tree, making this less technically challenging than I had expected. However, this project provided good insight on the impact of choosing good constraints, which make a project enjoyable and encourage thinking design-first instead of implementation-first.
For example, after I scrapped the BASIC-clone, I started by writing the language specification first and then implementing the interpreter. This is much more disciplined than I usually am when programming, and I enjoyed it.
Overall, it just felt good to see a project through from start to finish. I’m also interested to see how usable this language is – it was pretty difficult writing a simple hello world!