-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Durtle is a turtle-oriented language, meaning that its primary focus is drawing programmatically.
Durtle has some (intentional) limitations:
- Angle movement is limited to multiples of 90 degrees
$\left(\frac{\pi}{2}\right)$ - There is only one variable available for modification/storage
- There are (only?) 25 color options (one for each alphabetical character except
v)
The angle limitation is due to the way Durtle is written - movement is restricted to the cardinal directions.
The single-variable constraint is meant as a challenge - how complex of a program can you write using only one variable at a time? The idea is to force users to rely on passing colors to functions and/or using the pixels colored by the turtle as memory.
The last limitation is due to Durtle's syntax, which uses the letter v for the "move down" command.
The syntax of Durtle is best described with the help message (viewable using ??):
Help for durtle
---------------
Notes:
X is anything that evalues to a number
C is a color (any lowercase letter besides 'v')
N is a number (integer)
Functions cannot be defined inside a function definition
Movement and direction:
>X Move X units right
<X Move X units left
^X Move X units up
vX Move X units down
|X Add X * 90 to the angle in degrees
Pen and color:
. Pen down
, Pen up
C Select color
! Set color to color at current location
Variable:
:X Set variable to the numerical value of X
+X The value of the variable plus X
-X The value of the variable minus X
*X The value of the variable times X
/X The value of the variable divided by X
@X The value of the variable raised to the power X
$X The value of X raised to the power of the variable
%X The value of the variable mod X
_ The current variable value
Control flow:
[...]X Loop X times
[...]C Loop until current color is C
~X;...;...\ If/Then/Else with guard "variable == X"
~C;...;...\ If/Then/Else with guard "current color == C"
~X;...\ If/Then with guard "variable == X"
~C;...\ If/Then with guard "current color == C"
Output:
?; Toggle printing style (default is no line breaks)
?? Print this help message
?! Print the current color name
?_ Print the variable value
?: Print the variable's ASCII character
?| Print the current heading as a multiple of 90 degrees
Functions:
(...)N Define a function with ID N
{CX}N Call function N with parameters C and X
Miscellaneous:
` Begin/end a comment
The below examples illustrate some fractals created using recursion. You can see the images for these in the README. Try running them in the REPL!
(~4;[>1^2]_[>1v2]_<*2;{#/2}1,>_.{#/2}1,</2^_.{#/2}1,</2v_.\)1.{g512}1
(~1;>_;v_>_<_{#/3}1>/3{#/3}1^_\)1.{a243}1
(~4;.>_v_<_^_,;{#/2}1>/2{#/2}1v/2{#/2}1</2{#/2}1^/2\)1{b128}1
(~0;_;~a;|1;|3\~a;{r-1}1;{a-1}1\^2~a;|3;|1\~a;{a-1}1;{r-1}1\^2~a;{a-1}1;{r-1}1\~a;|3;|1\^2~a;{r-1}1;{a-1}1\~a;|1;|3\\)1.{a8}1
There is a color for each letter of the alphabet, except v (because v is used as the "move down" command).

The background color is white by default. The initial pen color is also white (so drawing will not have any effect until a color is selected).
Other than that, colors come from the xkcd list. Here is the mapping from character to color name:
const string[char] colorNames = [
'a': "azure", 'b': "black", 'c': "cyan",
'd': "denim", 'e': "eggplant", 'f': "fawn",
'g': "green", 'h': "heliotrope", 'i': "indigo",
'j': "jade", 'k': "kiwi", 'l': "lichen",
'm': "magenta", 'n': "navy", 'o': "orange",
'p': "pink", 'q': "turquoise", 'r': "red",
's': "silver", 't': "teal", 'u': "umber",
'w': "white", 'x': "bordeaux",
'y': "yellow", 'z': "maize"
];And here is the mapping from character to RGB color:
const Color[char] colorMap = [
'a': Color(6, 154, 243), 'b': Color(0, 0, 0),
'c': Color(0, 255, 255), 'd': Color(59, 99, 140),
'e': Color(56, 8, 53), 'f': Color(207, 175, 123),
'g': Color(21, 176, 26), 'h': Color(217, 79, 245),
'i': Color(56, 2, 130), 'j': Color(31, 167, 116),
'k': Color(156, 239, 67), 'l': Color(143, 182, 123),
'm': Color(194, 0, 120), 'n': Color(1, 21, 62),
'o': Color(249, 115, 6), 'p': Color(255, 129, 192),
'q': Color(6, 194, 172), 'r': Color(229, 0, 0),
's': Color(197, 201, 199), 't': Color(2, 147, 135),
'u': Color(178, 101, 0), 'w': Color(255, 255, 255),
'x': Color(123, 0, 43), 'y': Color(255, 255, 20),
'z': Color(244, 209, 84)
];There are currently 5 different log levels:
const enum LogLevel {
// log everything
LOGGING_ALL = 0,
// log everything except movement instructions
LOGGING_NO_MOVEMENT = 1,
// log only the generated parse tree
LOGGING_PARSETREE = 2,
// the REPL default is to just log the parse tree
LOGGING_REPL = 2,
// log nothing
LOGGING_DISABLED = 3
}Additionally, the runProgram function takes a boolean parameter logToFile, which will write the logs to a file log/log_<iso datetime string>.txt instead of stdout.
Calling runRepl will open a REPL session in the shell, as well as a corresponding raylib window running in another thread. Currently, the REPL treats each entry as a completely new program and the raylib window is wiped each time; that may be changed in the future.
The main benefit of using the REPL is that it is much faster than building Durtle with a hardcoded program - the raylib window only opens once, and is then listening for pixel array updates passed by the main thread. This is very useful when trying to get a program to work.
The input is printed again at the end of any parse output, so that the user can easily copy it to modify it if their shell does not support command history.
To exit nicely from the REPL and close the UI window, type either exit or quit in the REPL and hit Enter.
Sample REPL session screenshot:

The raylib UI has some minimal controls to move around and zoom in/zoom out:
- Zoom in: Up arrow key
- Zoom out: Down arrow key
- Pan up/left/down/right: WASD keys
- Quit: Escape key
Durtle is parsed using a parsing expression grammar (PEG), which is a specification similar to BNF but can be used directly by a PEG parser (such as Pegged, the parser used by Durtle) to parse a program without having to write a lexer or parser manually.
Here is the PEG specification for Durtle:
Turtle:
Terms < (Instruction)+
Instruction < Right / Left / Up / Down
/ Loop / Color / Set / Get
/ Add / Sub / Mul / Div / Pow
/ RPow / Mod
/ PenDown / PenUp / PrintCmd
/ Conditional / FuncDef
/ FuncCall / Comment / Exp
/ AngleChange / ColorCond
AngleChange < "|" Exp
Comment < "`" (!"`" .)* "`"
FuncDef < "(" FuncTerms ")" Number
FuncTerms < FuncTerm+
FuncTerm < Right / Left / Up / Down
/ FuncLoop / ColorExp / Set / Get
/ Add / Sub / Mul / Div / Pow / RPow / Mod
/ PenDown / PenUp / PrintCmd
/ FuncCond / FFuncCall / FuncExp / Comment
/ FColorCond / AngleChange
FuncLoop < "[" FuncTerms "]" (FuncExp / ColorExp)
FuncCond < FuncIfElse / FuncIf / FColorIfElse / FColorIf
FuncIfElse < "~" FuncExp ";" FuncTerms ";" FuncTerms "\\"
FuncIf < "~" FuncExp ";" FuncTerms "\\"
FFuncCall < "{" ColorExp FuncExp "}" Number
FuncCall < "{" ColorExp Exp "}" Number
Conditional < IfElse / If
ColorCond < ColorIfElse / ColorIf
FColorCond < FColorIfElse / FColorIf
IfElse < "~" Exp ";" Terms ";" Terms "\\"
If < "~" Exp ";" Terms "\\"
ColorIfElse < "~" ColorExp ";" Terms ";" Terms "\\"
ColorIf < "~" ColorExp ";" Terms "\\"
FColorIfElse< "~" ColorExp ";" FuncTerms ";" FuncTerms "\\"
FColorIf < "~" ColorExp ";" FuncTerms "\\"
ColorExp < "#" / Color
PrintCmd < "?_" / "??" / "?!" / "?:" / "?;" / "?|"
PrintToggle < ";"
Right < ">" Exp
Left < "<" Exp
Up < "^" Exp
Down < "v" Exp
Loop < "[" Terms "]" (Exp / ColorExp)
Color < [a-z]
Set < ":" Exp
Get < "!"
PenDown < "."
PenUp < ","
MathExp < Add / Sub / Mul / Div / Pow / RPow / Mod
Add < "+" Exp
Sub < "-" Exp
Mul < "*" Exp
Div < "/" Exp
Pow < "@" Exp
RPow < "$" Exp
Mod < "%" Exp
Exp < Number / Var / MathExp / FuncCall
/ Conditional / ColorCond
FuncExp < Number / Var / MathExp / FuncCall
/ FuncCond / FColorCond
Var < "_"
Number < ~([0-9]+)