Using Soweli as a mascot, I wrote some code and a sequence of instructions for implementing a tutorial. Steps of the tutorial are automatically triggered when the web interface detects that the user has completed them.
Clicking on Soweli dismisses or brings up the tutorial. It guides you through how to draw basic shapes and then how to make your own custom functions, before pointing you to the quick reference sheet, another new addition to the page. The quick reference sheet is basically just a really short and simplified version of the specification.
Yesterday I interrupted my friends’ online Friday night hangout to make them write some code in Poki Nanpa. A couple of them have some experience with coding and know the basics of Toki Pona. After explaining the basics I got two usable programs out of them: “circles.poki” and “ball.poki”. I’ll include both of them in the example programs folder.
The programs aren’t massively complicated since, understandably, everyone wanted to get back to playing Super Battle Golf. But I’m hoping this has demonstrated that someone unfamiliar with the language can figure out how to write code in it.
Using the saving and loading functions I developed, I wrote a longer and more complex program using Poki Nanpa than the ones I’ve previously been testing with. This is an implementation of Conway’s Game of Life. It will be available in the example programs folder when I submit the project.
As well as just being a cool program, this helps verify that several things are working. It pushes the JavaScript implementation of Poki Nanpa a bit in terms of performance, since it’s having to operate on the entire canvas every tick. It also makes sure that everything is working with regard to the canvas wrapping around.
Here’s a fleet of gliders demonstrating the wrapping canvas.
Before now programs disappeared if you didn’t manually copy them into a text file before closing the browser. Now, however, there are buttons to let you save and load Poki Nanpa files into a bespoke file format called .poki.
Not only does this save the program, it also saves the state of the canvas. So if you’re sharing a program with someone, you can put stuff in the canvas and they’ll be able to see it when they load up the program.
To save the canvas I used simple run-length encoding. The canvas isn’t that huge, so this is a perfectly sufficient form of compression. A .poki file also saves the position of the cursor. Handling the files in JavaScript was a little fiddly but I found this StackOverflow answer that did exactly what I wanted by creating a dummy download link to load the data into.
Loading is just all the same steps in reverse. Now I can start creating example programs in a consistent format that the user can load directly rather than needing to copy-paste the actual text.
I found a JavaScript library called CodeMirror which provides a neat text input area specifically designed for writing code. It has line numbers and so on, and you can write a parser for styling different kinds of words which I might do if I have time.
This was one of the issues brought up in testing so I’m glad there’s a library for it. Today I also implemented a button that clears the canvas, and a sleep function! That’s big news because it means you can now animate things and program video games in Poki Nanpa. That was one of my “could have” goals that I wasn’t sure would be part of the final project.
Getting everything to update correctly between asynchronous functions was a pain, because I haven’t worked with them much before, but I feel like I have a better understanding now and I’m pleased with how it turned out.
Today in class we were encouraged to test each other’s projects and I got the opportunity to see how other people interact with the Poki Nanpa web interface. I gave them the following list of instructions (there was actually more detail but I’m omitting the specifics):
Try moving the cursor programmatically; introducing the user to the concept of the cursor and the idea of using a program to change the output canvas.
Try drawing a circle; introducing a drawing function.
Try drawing a rectangle; introducing functions that take multiple parameters, since those are formatted differently to single-parameter functions.
Try drawing on the canvas directly using the colour palette; introducing the idea that the canvas can be used as an input.
Try making a function; introducing the idea that the user can create custom functions and combine them.
The feedback was mostly positive except for some issues I already plan to fix; namely, not being able to drag the mouse to draw, the lack of indicators for which colour is selected, and the lack of line numbers. The “stop” button also doesn’t do anything yet because there are no functions to make a program not execute instantaneously, but that will change soon.
In terms of the design of the language, the most common point of confusion was the difference in parameter order for single-parameter functions as opposed to multi-parameter functions. The language was designed this way to reflect the grammar of Toki Pona, but for non-speakers it comes across as confusing. If further iterating on the language design in the future, this could be something to consider dropping, but I think I am going to keep it for now.
What I was most pleased about was the fact that two of the participants kept playing around with the language after they’d completed the tasks I gave them. Here’s a face that one of them drew using the shape tools:
One participant refused to write code, saying they weren’t a programmer, which is a strange thing for someone on our course to say… they liked the interface though.
Initially the program was interacting directly with the HTML svg element to draw pixels. This worked fine, but for easier manipulation of the canvas as I start adding more advanced program features, I wanted to have an array of pixels that I could change programmatically, which would then be pushed to the canvas element as needed.
This also gave me the opportunity to implement the user being able to draw on the canvas directly. That’s now working great. Here’s some “art” I drew during testing:
A more interesting side effect of this is that I didn’t just make a 2D array; I made a 3D array. The canvas has layers, and the cursor will be able to traverse through them just like it can traverse in the other two dimensions. This means that programs can potentially store data, or even sprites, on other layers for copy-pasting to the visible layer during execution. This has a lot of potential applications, and gives users a way of storing data considering Poki Nanpa has no variables.
Because Toki Pona has a limited number of words, and few of them are actually related to programming, I have to be selective about which ones I use to mean certain things. I encountered a key example in March when I realised that I couldn’t use anu to mean both “else” in a conditional statement as well as “or” in a Boolean operation, because there was no way to disambiguate the two.
Instead I decided to use “a” for the conditional statement. In Toki Pona, “a” is a general-purpose exclamation. Here it’s being used to represent an interruption to what was expected. These are the kinds of concessions I’m making as I map Toki Pona words to Poki Nanpa functions.
Another concession is that I’ve scrapped the idea of having no brackets. You can still write a program just fine without them, but they make functions a lot more concise and make it a bit clearer to tell what’s going on. I considered using Toki Pona words to mark the beginning and end of a bracketed segment instead, but decided against this because there was nothing that would have worked grammatically. For example, I could have used pini (“finish”) to let the programmer indicate when a block is done. But reading that as a grammatical sentence in Toki Pona would apply the adjective “finished” to whatever expression it followed, which usually doesn’t make sense. In the end, brackets ended up being the most elegant solution.
Toki Pona itself, like English, has the kinds of grammatical ambiguities that a programming language can’t handle if you want it to do the same thing every time you run it.
There is a standardised way to turn a specification of a language into a parser that can take program code and turn it into something that can be executed by a program. The two main steps are tokenisation into a list of tokens, followed by parsing into an “abstract syntax tree”; a data structure representing the shape of a program.
After beginning by trying to invent a parser that used string operations to try to build an abstract syntax tree from scratch, I instead switched to following a more standard method, outlined in this helpful guide. This involves a parser that “eats” tokens one by one according to what is expected based on what kind of language feature the parser thinks it is currently parsing. If it encounters an unexpected token, it throws a syntax error.
I am implementing this parser in JavaScript as part of the web interface I started working on, but the idea of a specification is that someone else would be able to come along and implement Toki Nanpa in any other environment.
Since Poki Nanpa is a functional language, everything happens at once. That puts it at odds with most graphical programs, which require a sequence of instructions. The solution is to use the functional aspects of Poki Nanpa to construct a list of instructions, which is then executed in sequence. To pull that off, it needs to be able to handle lists.
In the specification, I have outlined the list operations that will be available in Poki Nanpa. These include adding and removing elements, mapping one list to another using a defined function, and passing the elements of lists as parameters into functions. That also meant coming up with a way to specify whether a function is meant to be evaluated or passed when it is used as a parameter. Right now I’m not sure if the solution I’ve come up with is airtight, which is why it’s time to start implementing the interpreter and finding the gaps and loopholes in the specification.