xTurtle Info


The xTurtle Web App lets you write code in a simple programming language, which is also called xTurtle. This page contains descriptions of the xTurtle programming language and of the web app. Note that you can get a lot of information by reading the tutorial examples that are available when you run the app.

I invented the xTurtle programming language in 1995 to use to use as an example in a textbook that I was writing a the time, The Most Complex Machine, a book that surveyed the field of computer science. Since programming is only one of the topics in the book, I wanted a reasonably simple language, but one that would have the major features of a typical programming language, including variables, assignment statements, loops, if statements, recursive subroutines, and even some multitasking. However, xTurtle does not include objects or data structures of any type. And because it is old, the syntax diverges significantly from the syntax of common programming languages like C and Java, most notably perhaps in the use of ":=" instead of "=" for assignment.

The first xTurtle program was written in the Pascal programming langauge to run on Macintosh computers. Soon after the World Wide Web was invented, it was translated an "applet" written in Java, so that is could run in a web page. However, Java applets are no longer supported by web browsers. The new version is a translation into JavaScript and should work in all major web browsers.


The xTurtle Web App

This section is a meant as a brief user manual for the xTurtle app. The next section is a description of the xTurtle programming language.

The web page where xTurtle runs has two main sections. The top section is used to write and view programs written in the xTurtle programming language. The bottom section contains the drawing surface where the results of program execution are shown. I will discuss the bottom section first.

The drawing surface is shown as a large white square. When the page first loads, the "turtle" is shown as a triangle at the center of the square, pointing to the right. The turtle can move around in the drawing surface. Usually, it draws a line as it moves. The turtle can respond to a number of commands, such as forward(5), which tells the turtle to move forward 5 units, and turn(45), which tells the turtle to rotate counterclockwise through an angle of 45 degrees. The turtle draws red lines, unless you tell it to use a different color.

The position of the turtle is given by a pair of coordinates, (x,y). The x coordinate for the area visible in the drawing surface ranges from -10 at the left to 10 at the right, and the y coordinate ranges from -10 at the bottom to 10 at the top. The turtle can move outside the visible region, and will just keep drawing happily, even though you can't see what it's doing. The command home moves the turtle back to the center of the screen.

Above the drawing surface is an input box where you can type in commands for the turtle. When you press return, the turtle will carry out the commands or tell you that there was an error in your input. Clicking on the button named "Do It!" is equivalent to pressing return. If an error message is displayed, you can make it go away by clicking on it. But it will also go away automatically the next time you press return or click the "Do It!" button.

There are no restrictions on what you can type in the command-input box. You can even put a whole subroutine definition there. However, for anything that takes more than a few words, you'll want to write a program.

The top section of the web page contains a large text area for writing xTurtle programs. When the web page is first loaded, it should show a sample program named "Tutorial 1: Basics". Other sample programs are available and can be viewed by selecting them from the pop-up menu above the text area.

If you want to create a new program of your own, select "[New Program]" from the pop-up menu. The text area will be cleared, and you can enter your own program. The "Save" button lets you save the program from the text area to a local text file on your computer. The "Load" button lets you select a text file on from your computer to be loaded into the text area. In most browsers, saving and loading files will use file save/open dialogs similar to those used in other programs. (Some browsers still use an older kind of file handling. In those browsers, saving a file will look just like downloading a file from the Internet, and loading a file will look like uploading a file to the Internet.) Saving and loading files is new in November, 2023.

To run the program on the text area, just click on the "Run the Program" button. If the program is written correctly, it will be compiled and executed. If there is an error, you'll get an error message, which will be shown below the "Run the Program" button. If there is an error, the computer will move the blinking cursor to the position in the program where it noticed the error. The browser should scroll the text, if necessary, to show the cursor (but some browsers might not do that).

Once the program has finished executing, you can run it again by clicking the "Run the Program" button again. Before running a program, the computer always clears the screen and restores the turtle to its initial state (at the point (0,0), facing right, with pen down, and drawing color set to red). If you've just run a program that defines subroutines or variables, you can use them in any commands that you type into the command-input box. However, they will be cleared out of memory if you run another program or click the "Reset" button.

The button named "Indent the program" can be used to fix the indentation of the program in the text area, to show its structure. This is only useful for programs that you are writing yourself. Indenting the program can help you to check whether your statements are properly nested.

Finally, here is some information about the buttons and other controls that appear to the left of the drawing area


If you use xTurtle on your own web site, note that it requires the programs folder, which contains the sample programs for the file menu. It is possible to run the app without the sample programs by adding "?files=none" to the URL. It is also possible to use different sample programs, as in this example:

Run xTurtle with an alternate set of sample programs.


The xTurtle Language

What follows is somewhat informal, but fairly complete, specification of the xTurtle programming language, as implemented in the xTurtle web app. For many purposes, you might find the turtorial examples from the web app more useful. You might want to look at those examples first, in any case.

Program Structure

A program can contain comments. A comment begins with { and ends with }. Comments can be nested. Comments are for human readers only. They are ignored when the program is run.

The layout of a program on the page is ignored. You can have more than one command on a line, and you can split commands over several lines. You can't have spaces in the middle of words. (The command PenUp, for example, cannot be written as "Pen Up".) Names are not case sensitive; that is, no distinction is made between upper and lower case letters. (So you can write PenUp, penUp, penup, or even PeNUp, and they will all mean the same to the computer.)

A program consists of a sequence of one or more of the following: statements, variable declarations, subroutine declarations, function declarations. Subroutine and function declarations cannot be nested inside one another. (To provide for mutual recursion among subroutines and functions, they can be "predeclared." This will be covered below.) The program is executed from beginning to end. Declarations are much like statements in that you can think of them as being executed. That is, you can't use a variable, subroutine, or function until it has been declared.

Identifiers and Reserved Words

Certain words are reserved for special purposes in the xTurtle language. Reserved words cannot be used as names for variables, subroutines, and functions. Note that since upper and lower case are equivalent, reserving the word "declare" also reserves "DECLARE", "Declare", and so on. Reserved words in xTurtle include:

(Combined words like "endif" and "endfunction" are redundant, since they can all be written equivalently as two words: For example, "end if". Probably, it was a mistake to include these combinations in the language, but there they are. I will not mention them again; instead, I will always use two separate words.)

Variable names, subroutine names, and function names are collectively known as identifiers. You can make up your own names for the variables and routines that you declare, as long as you don't use reserved words and as long as you don't try to reuse a name in the same program. Identifiers must begin with a letter. They can contain letters, digits from 0 to 9, and the underscore character (_). They cannot contain spaces or other white space. They can be of any length. A given name can only have one declaration.

Variables and Expressions

Before a variable can be used, it must be declared. This is done using a variable declaration, which consists of the reserved word, declare, followed by the names of one or more variables that are being declared. Variables in the list should be separated by commas. For example:

                  DECLARE InterestRate
                  DECLARE x, y, row, column

Variable declarations cannot be nested inside other statements, such as loops. They can occur in subroutines and functions (where they are used to create "local variables," as discussed below).

In xTurtle, the value of a variable must be a real number such as 42, 3.14159, -1, or 12.3e-12. The last example used scientific notation, which is legal in an xTurtle program. (The notation 12.3e-12 means 12.3 times 10 raised to the power -12. Very large and small numbers are written using scientific notation.) When a variable is first declared, it has a special value called "not-a-number" It is illegal to use such a value in a computation, and doing so will result in an error that will crash your program.

Variables and numbers can be used in mathematical expressions such as (1 + InterestRate) * Principal. Expressions can include the usual arithmetic operators plus (+), minus (-), times (*), divide (/), and exponentiation (^). Expressions can also include built-in functions and user defined function. For example: sin(2*angle+30). Parentheses are always required around the arguments of a function. (In the case of a function that takes no argument, a set of empty parentheses is optional.) The predefined functions are:

Note that the names of these predefined functions are reserved words.

There are a few reserved words in xTurtle that act like pre-defined read-only variables. These read-only variables contain information about the current state of the turtle. You can inspect the values of these variables, but you can't use assignment statements to change their values. The read-only variables are:

Assignment Statements

The value of a variable can be changed by using an assignment statement. An assignment statement takes the form

              <variable> := <expression>

where <variable> is any declared variable (or the name of a parameter in a user-defined subroutine or function) and <expression> can be a number, a variable, or any expression created using operators and functions, as described above. Here are some example assignment statements:

               Rate := 0.7
               y := 3*x^2 - 2*x + 1
               count := count + 1
               r := exp(theta)

Built-in Subroutines

The xTurtle language includes many predefined subroutines. Most of the predefined subroutines are turtle graphics commands which move or turn the turtle or affect its state. Two predefined subroutines related to multitasking, Fork(n) and KillProcess, are discussed later. The others built-in subroutines are:

Input/Output

The input/output commands in xTurtle are pretty rudimentary. However, it is possible to display messages to the user and to read numbers input by the user. There are four built-in subroutines for doing such input and output. These subroutines are special in that they use strings. A string is a sequence of characters to be displayed to the user. (A string cannot include an end-of-line.) For example, the command

             TellUser("Hello World!")

will display the characters

             Hello World!

to the user. Note that in the program, the string is enclosed in double quotes, but that the quote characters are not part of the string that is displayed to the user. There are rules for converting the string in the program to the string to be displayed to the user: To display a quote character to the user, you have to use two quote characters in the string in the program. For example,

             TellUser("I said, ""Stop!""")

will display

             I said "Stop!"

The value of a variable can be included in the displayed string. To do this, you have to include a # character followed by the variable name in the string in the program. This works for declared variables as well as for the predefined read-only variables. For example,

             TellUser("The turtle is at (#xcoord,#ycoord).")

will tell the user the current position of the turtle. That is, when the string is displayed, the value of xcoord will be substituted for #xcoord in the string, and similarly for ycoord. (If you want to display an actual # character, you have to write it as ## in the string.)

The TellUser command pops up a box to display its string. The user must click on an OK button to dismiss this box, and the program waits for the user to do so. The display is not changed.

There is also a command for displaying a string in the graphics display area. This DrawText command writes the string at the current turtle position in the current drawing color. The string is drawn even if the turtle's pen is currently up. After drawing the string, the turtle moves to a point just below its original position, so that the output of successive DrawText commands will line up neatly one under the other. DrawText has one parameter specifying the string to be drawn. For example,

             DrawText("Hello World!")

There are commands for doing input: AskUser and YesOrNo. Each of these input commands has two parameters. The first parameter is a string to be displayed to the user. This string is meant to prompt the user for a response. The second parameter is the name of a variable where the user's response is to be stored. For AskUser, the user can type in any real number as a response. For YesOrNo, the user is given the choice of responding yes or no. If the user says yes, the value 1 is stored in the variable; if the user says no, the value 0 is stored. For both of these commands, the computer pops up a box to display the string and get the user's response. The program waits until the user responds. The display is not changed. Here are some examples of using these two subroutines:

              AskUser("What is the interest rate?", rate)
              AskUser("Enter a number less than #max", x)
              YesOrNo("Do you want to play again?", response)

Note that the strings used in these commands can include the values of variables, just like the strings in TellUser and DrawText.

Logical Expressions

LOOP statements and IF statements (described below) use logical expressions to test whether or not some specified condition is true. A logical expression is a formula that can be either true or false. Basic logical expressions are formed by comparing numerical values using the relational operators =, <, >, <=, >=, and <>. (The last three of these mean "is less than or equal to", "is greater than or equal to", and " is not equal to", respectively.)

Basic logical expressions can be combined into more complex expressions using the logical operators and, or, and not. (These can also be written as single characters: &, |, and ~.)

In the absence of parentheses, the precedence ordering for operators in xTurtle, from highest to lowest, is:

             NOT
             AND
             OR
             relational operators
             ^
             * and /
             + and -

LOOP Statement

To repeat a sequence of statements in xTurtle, use a LOOP statement, which consists of the reserved word loop, followed by the statements to be repeated, followed by end loop. One of the statements in the loop must be some sort of EXIT statement, which causes the loop to terminate (either unconditionally or conditionally) and transfers control to the statement that follows the loop. Statements can be nested. An EXIT statement always exits from the innermost enclosing loop. There are three forms of the EXIT statement:

                  EXIT
                  EXIT IF <condition>
                  EXIT UNLESS <condition>

The plain EXIT statement exits the loop unconditionally, and would ordinarily be used inside an IF statement that is nested inside the loop. In the other two forms of the EXIT statement, the <condition> is a logical expression, as defined above. An EXIT IF statement exits its loop if its condition is true; an EXIT UNLESS statement exits its loop if its condition is false.

Here are two simple example programs that use loops:

       DECLARE ct                       DECLARE length
       ct := 0                          LOOP
       LOOP                                EXIT IF 1=2  { loop forever! }
          forward(1)                       length := 7*random  { 0 <= length < 7 }
          turn(45)                         hsb(random,1,1)
          ct := ct + 1                     forward(length)
          EXIT IF ct = 8                   back(length)
       END LOOP                            face(360*random)
                                        END LOOP

IF Statement

An IF statement is used to choose one of several alternative courses of actions. An IF statement always starts with a test of the form

                IF <condition> THEN

and ends with

                END IF

The end if is not an independent statement. It simply marks the end of the IF statement. Between the if and the end if, there are lots of options. Here are some examples that exhibit the options, with comments that explain what they mean:

      IF d >= 0 THEN   { Simple choice: do the following statements or skip them }
         r1 := (-b - sqrt(d))/(2*a)
         r2 := (-b + sqrt(d))/(2*a)
      END IF
         
          
      IF n/2 = trunc(n/2) THEN  { Branch: if the condition is true, do the   }
          n := n/2              {         statements between THEN and ELSE;  }
      ELSE                      {         if the condition is false, do the  }
          n := 3*n+1            {         statements between ELSE and END IF }
      END IF
      
      
      IF grade > 90 THEN           { Multiway Branch: Each of the conditions }
         TellUser("Grade is A")    {   is tested in turn.  As soon as one is }
      OR IF grade > 80 THEN        {   found that is true, the statements    }
         TellUser("Grade is B")    {   following that condition's THEN are   }
      OR IF grade > 70 THEN        {   executed, and then the computer jumps }
         TellUser("Grade is C")    {   out of the IF statement to whatever   }
      OR IF grade > 60 THEN        {   statement follows the END IF.  If     }
         TellUser("Grade is D")    {   none of the conditions are true,      }
      ELSE                         {   then the statements between ELSE and  }
         TellUser("Grade is F")    {   END IF are executed.  The ELSE part is}
      END IF                       {   optional.  If it is absent and if all }
                                   {   the conditions are false, then none   }
                                   {   of the statements within the IF       }
                                   {   statement are executed.               }

User-defined Subroutines

The xTurtle language has built-in subroutines like PenUp and moveTo(x,y). It is possible to define new subroutines in a program. A subroutine has a name and, optionally, a list of parameters. Once a subroutine has been defined, it can be called by giving its name and — if it has a parameter list — a list of values to be used for its parameters. A subroutine consists of a list of statements and variable declarations. When the subroutine is called, all the statements within the subroutine are executed.

Variables declared within a subroutine are called "local variables" for that subroutine. They are not visible from outside the subroutine and are deleted from memory when the subroutine ends. Variables that are not defined within a subroutine are called "global variables." A subroutine does not have automatic access to global variables. However, it is possible to give a subroutine access to global variables by explicitly "importing" them into the subroutine. This is done with an IMPORT statement, which consists of the reserved word import followed by the names of one or more previously declared global variables (separated by commas). IMPORT statements can only occur inside subroutine and function definitions.

A subroutine definition starts with the word sub, followed by the subroutine name, followed optionally by a list of parameters. The parameter list is just a list of parameter names, separated by commas. A parameter name can be optionally preceded by the reserved word ref, which indicates that the parameter is to be passed by reference. (This is discussed below.) The word sub, the subroutine name, and the parameter list make up the "subroutine header." Following the header come the statements that make up the subroutine. Finally, end sub is used to mark the end of the subroutine. Here are two sample subroutines:

      SUB polygon(N,side)                   SUB UpdateAmount(ref amount)
         DECLARE count                         IMPORT InterestRate
         count := 0                            DECLARE interest
         LOOP                                  interest := InterestRate * amount
            forward(side)                      amount := amount + interest
            turn(360/N)                     END SUB
            count := count + 1
            EXIT IF count = N
         END LOOP
      END SUB

When a subroutine is called, one parameter value must be provided for each parameter listed in the subroutine definition. The parameter values in the subroutine call statement are called "actual parameters." Parameters can be passed by value or by reference, as indicated by the absence or presence of the reserved word ref in the subroutine definition. When a parameter is passed by reference, the subroutine can change the value of an actual parameter that is provided to it when the subroutine is called. (The actual parameter for a ref parameter must be a name; it cannot be a constant or a complex expression.) This is illustrated by the UpdateAmount example given above.

It is possible to exit from a subroutine at any point by using a RETURN statement, which consists simply of the word return. RETURN statements can only occur in subroutines. When the computer executes a RETURN statement, it exits immediately from the subroutine.

A subroutine can call itself. This is called "recursion." It is also possible for one subroutine to call another which in turn calls the first subroutine. Longer loops of subroutine calls are possible. This is called "mutual recursion." Because subroutines must be declared before they are used, a special syntax is required to make mutually recursive subroutines possible. One of the subroutines must be "predeclared". This is done by giving the reserved word predeclare, followed by the subroutine heading. The rest of the subroutine is omitted. Predeclaring a subroutine allows it to be called by other subroutines. A full definition of the predeclared subroutine must be given later in the program. The full definition includes another copy of the subroutine header.

User-defined Functions

A function is very similar to a subroutine, except that it computes and returns a value. A function in xTurtle is defined in the same way as a subroutine, with the word function substituted for the word sub. The only other difference in the definition is that a function must include a RETURN statement that specifies the value to be returned by the subroutine. A RETURN statement in a function takes the form

                 return <value>

where <value> is a constant, variable, or formula specifying the value to be returned. Here are two sample functions:

      FUNCTION NextN(num)                    FUNCTION UpdateAmount(amount)
         IF num/2 = round(n/2) THEN              IMPORT InterestRate
            return num/2                         DECLARE Interest
         ELSE                                    Interest := amount * InterestRate
            return 3 * num + 1                   DECLARE newAmount 
         END IF                                  newAmount := amount + Interest
      END FUNCTION                               return newAmount
                                             END FUNCTION

User-defined functions are used in the same way as built-in functions such as sin(x). Functions can have ref parameters, they can be recursive, and they can be predeclared.

Multitasking

In "parallel processing," several processes are going on at the same time. "Multitasking" is a way of simulating parallel processing by giving a little bit of execution time to one process, then a little bit to another process, and so on. Multitasking can be done in xTurtle by using the fork statement. Fork is a subroutine that takes a single parameter, specifying the number of processes to be created. This number must be between 1 and 100. Conceptually, the command fork(N) splits a turtle into N different turtles. Each of the turtles then proceeds to execute the following statements independently. Any variables that exist before the fork are shared by all the turtles. However, if a variable declaration statement occurs after the fork, each turtle will create its own copy of the variable.

Each process created in a fork command continues executing until either: it reaches the end of the program, or it executes a KillProcess command, or it finishes the subroutine or function in which the fork command occurred. Note how forks in subroutines are handled: All processes that are created inside the subroutine must end before the subroutine returns. When the subroutine returns, only the original process that called the subroutine is still running.

In fact, the command fork(N) actually creates N "child processes." The original parent process goes to sleep until all the child processes have ended. Then the original parent awakens. The state of the turtle in the awakened parent process is the same as it was before the child processes were created. That is, the turtle still has the same heading, position, visibility, pen state, and drawing color as it did at the moment when the fork statement was executed. If the fork command occurred inside a subroutine, the subroutine does not return until all the child processes have ended, and then it is actually the original parent process that returns. (The same sort of thing happens if you use a fork command in the xTurtle web app's command-input box.)

The processes that are created by a fork command are identical, except for one thing: Each process has a different value for the read-only variable forkNumber. The fork numbers for the processes created by the command fork(N) range from 1 to N. The different values for forkNumber allow the different processes to do different things. Note that it is certainly possible to have two or more forks in a row. However, a process only remembers the fork number from the most recent fork command that it has executed.

Grab Statement

After a fork command has been executed, the processes that it creates can communicate by setting or reading the values of shared variables (variables that were declared before the fork command). This form of communication has the problem of "mutual exclusion" — making sure that only one process at a time has access to the shared variable. It is up to the programmer to enforce mutual exclusion. In xTurtle, this can be done using the GRAB statement, which takes the form

            GRAB <variable> THEN
                .
                .  { any statements — except fork, exit, return }
                .
            END GRAB

The <variable> in a GRAB statement must be a global variable. If you want to use a GRAB statement inside a subroutine, you will have to use an imported global variable. The point here is that only one process at a time is allowed to grab a given variable. If a process tries to grab a variable and another process has already grabbed it, then the second process has to wait until the first process exits from its GRAB statement.

There is a variation of the grab statement that has an ELSE part. (I have never found it actually useful.) It has the form:

            GRAB <variable> THEN
                  { some statements }
            ELSE
                  { more statements }
            END GRAB

In this case, if the GRAB fails, the computer does not wait. Instead, it executes the ELSE part of the grab statement. The ELSE part can include fork, exit, and return statements.


David Eck