While learning about the power of dialects in Rebol, I decided to write a little Logo interpreter to put what I learned to use.
You can get the source here: https://gist.github.com/mydoghasworms/f4fdde58882ecd14b2c9 (You need to run it with the last available Rebol/View 2.7 from rebol.com).
Apart from the time spent fiddling with the VID dialect to get the GUI right (which was mainly spent on getting the turtle to appear in the right place; the solution being to use translate prior to rendering the turtle image), the reason I did this was to try out the built-in parse function, which is what Rebol provides for you to create your own domain-specific language (DSL), which in the Rebol parlance is called a “dialect”.
In this case, I have implemented a little dialect for a Logo interpreter.
The code is fairly simple, I think. It consists in part of an object that defines a state and API for the turtle, including a command interpreter and a stack representing the history. In addition, it takes care of generating the VID dialect commands for the drawings created by the turtle.
The second part is the GUI definition which consists of a little drawing area, a history list and a field where you can enter the commands.
By taking this approach (separate object with API function), the code is a little longer than it probably needs to be, but I think it makes it fairly straightforward to understand.
The crowning glory of the program, however, is the part of the object that implements the little subset of the Logo dialect, which looks as follows:
parse-rules: [ some [ ['fd | 'forward] set value integer! (move-by value) | ['bk | 'back] set value integer! (move-by value * -1) | 'left set value integer! (turn value * -1) | 'right set value integer! (turn value) | 'color set value word! (set-color value) | 'home (reset) | ['pu | 'penup] (pen-is-down: false) | ['pd | 'pendown] (pen-is-down: true) | 'clear (clear) | 'repeat set value integer! set blk block! (repeat none! value [ parse blk parse-rules ]) | set word word! (last-error: to-string compose ["unknown word " (word)]) break ] ]
This little bit of code is what allows me to write a command like the following into the field:
repeat 9 [ fd 100 right 80 ]
and get this:
It’s not all sunshine and roses, of course. My biggest gripe with having the parse mechanism is the apparent inability to validate my input against the rules without executing stuff; a grievance I have aired here: http://stackoverflow.com/questions/28568589/rebol-2-using-a-parse-rule-to-check-input-without-executing-code. (Mind you, I am using Rebol 2; not sure if that will make a difference – and I will get shouted at for using it in a certain chat room on StackOverflow).
I think I have said this before, but I haven’t had as much fun learning a language since Ruby. And I haven’t felt as inspired as much by a language since then either. Thank you Carl Sassenrath, wherever you are! (Oh yes, at Roku, of course).