|
CIT 594
The "Bugs" Language
Spring 2008, David Matuszek |
This is a brief introduction to the "Bugs" language. "Bugs" is a language that I invented for the purposes of this course (CIT594 in 2008), and that you will be implementing.
Bugs is a special-purpose language whose raison d'être is simply to draw pretty patterns. A program in the language controls one or more "bugs" that move around the screen, optionally drawing colored lines as they go. While it is possible to use the language to do general computations, that isn't a very good idea.
Bugs live and move about in a square. The size of the square is 100 units
wide by 100 units high. Any bug whose x and y coordinates
are both between 0.0 and 100.0 will be (at least partially) visible on the
screen.When this square is displayed on the computer screen, the window
may be any number of pixels square, but from the program's point of view,
the top and left edges are always 0.0, and the bottom and right edges are
both at 100.0. As usual for graphics programs, the x coordinate increases
to the right, and the y coordinate increases downwards.
Bugs are not constrained to stay within this square; it's just that any bug that wanders outside it will not be visible on the screen. Hence, most programs should try to keep bugs within the square.
Bugs is "slightly" object-oriented. That is, there is an (optional) "Allbugs" class that contains variables and functions available to all bugs. The complete program consists of one or more "bugs" that inherit these variables and functions, and in addition can define their own variables and functions.
The Allbugs class looks like this:
Allbugs {
// zero or more variable declarations
// zero or more function definitions
}
Variables declared in the Allbugs class act like Java static variables;
that is, there is only one copy of each such variable, and it is shared by all bugs.
Each Bug has a name and up to four parts:
A Bug definition looks like this:
Bug sally {
// zero or more variable declarations
// an optional initialization block
// one or more commands
// zero or more function definitions
}
The above (if the comments are replaced by actual code) defines a Bug named
"sally". In this language, only Allbugs defines a class, while
Bug defines an object of that class, albeit one with its own unique
variables and functions.
A variable declaration is simply the keyword var followed by a list of variable
names, for example,
var distance, goal, danger
Variable names begin with a letter and may consist of letters, digits, and
underscores. All variables hold real (double)
numbers, and all numbers in a Bugs program are real numbers. Numbers written
as integers are still real numbers; for example 5 is the same as 5.0.
There may be multiple var declarations in a Bug. There is no difference between
declaring all the variables in one var declaration or in multiple var
declarations.
There are no private variables. A bug may refer to its own variables by name,
and it may refer to another bug's variables with dot notation, for example,
wilbur.goal.
Every bug has three predefined variables: x, y,
and
angle. The x and y variables
specify the bug's location, while the angle variable tells,
in degrees, which way it is facing. An angle of zero means the
bug is facing right (due east); positive values of angle are
counterclockwise, so that north is 90, west is 180, and south is 270.
The initialization block, if present, contains one or more commands, and looks like this:
initially {
// one or more commands
}
Only Bug definitions, not the Allbugs class, can have an initialization block. However the initialization block of a Bug can use variables and functions from both the bug being defined and the Allbugs class.
Commands are described in detail below. This part of a Bug definition constitutes the "main program" part of this bug.
The command part of a Bug is not set off by any special keyword; it is simply the sequence of commands following the initialization block (if there is one) and before the first function definition (if any functions are defined).
Functions defined in Allbugs are available to every bug, and behave exactly as if they were defined separately in each bug. This means, in particular, that if a bug uses a function defined in Allbugs, that function can use variables and functions defined in the bug. (Any reference to a variable that has not been declared, here or elsewhere, will result in a runtime error.)
Functions defined in an individual bug are private to that bug, and cannot be called from any other bug. However, as discussed above, functions defined in Allbugs act as if they were defined in the individual bug.
A function definition looks like this:
define functionName using parameter1, ..., parameterN {
// zero or more local variable declarations
// one or more commands
}
If the function has no parameters, the keyword using should be omitted.
Parameters and local variables are available only within the function, not from outside it.
Within a function, a statement of the form return expressionreturn statement,
a value of 0.0 is returned.
Commands are divided into two groups, "actions" and "statements." The distinction is that an action causes the bug to do something visible on the screen, and these actions must be coordinated with the actions of other bugs. A statement, on the other hand, is a purely computational step.
In the Bugs language, all values are kept as double values. A number may
be written with our without a decimial point, but may not begin with a decimal point.
The usual four arithmetic operations--add (+), subtract (-),
multiply (*), and divide (/)--are
provided, with the usual precedences. Parentheses may be used. Function calls
may be used in an expression, with the same syntax as in C.
There are no boolean values in the Bugs language. Numbers "close to"
zero are considered to be false, and all other numbers are considered to
be true. Conventionally, the number 1 is used when a true value
is required. With this representation,
&&) operation.||) operation.!x) operation can be implemented as
1 - x . These all work when "true" is represented by the number 1,
but may not work for other nonzero numbers.
The six standard comparison operators-- <, <=,
= (not == !), !=, >,
and
>= --are also provided. Each returns either 0 (false)
or 1 (true) as a result. Consequently, run-on comparisons such
as
x < y < z= will
return 1 (true) if the numbers being compared are within 0.001
of each other, and != will return 0 (false).
There are two built-in functions: direction(bugName) returns
the angle from this bug (that is, the bug calling the function) to the named
bug (0.0 being due east), and distance(bugName)
returns the distance from this bug to the named bug.
(Hence, if this bug executed the two commands and
, it would end up directly "on top of" that bug.)
An assignment statement consists of a single variable, followed by an
equals (=) sign, followed by an expression. For example,
x = 2 * (x + 1)
There is only one kind of loop, loop {...}, which can contain
any number of commands, plus zero or more
exit if condition
loop {
move 10
exit if x > 100
turn 90
move 10
exit if y < 0
turn -90
}
A switch statement consists of the word switch and braces
containing some number of case groups. For example,
switch {
case x < y
x = y
case x < z
temp = x
x = z
case 1 // 1 means "true"
z = y
}
The first case with a "true" (nonzero) condition is the one executed. Unlike
C or Java, there is no break statement, nor is there any need
for one; at most one
case will be executed. (If no case condition is met,
the statement has no effect.)
There is no if statement, but
switch {
case c
do_x
case 1
do_y
}
The do statement consists of the word do followed by a function call; the
result returned from the function is discarded.
A color statement consists of the word color followed
by a color name, such as red or black. This statement
changes the color that this bug draws in as it moves. Color names include
the fourteen defined in java.awt (but in lowercase and camelCaps),
plus whatever other colors you wish to define. The word none
acts as an additional color name.
As in C or Java, a function call may also be used as a statement; any result returned by the function is ignored.
The return statement was described earlier, and may only be used within a function definition.
An action is a command that results in a visible change on the computer screen.
The turn expression
The turnto expression
Both turn and turnto may take a value less than zero or
more than 360.0, and adjust it to be within this range. Both actions will also set
the bug's angle variable to a number in the range 0.0 to 360.0.
A bug's angle variable can be set directly by an assignment statement;
this also will adjust the value (if necessary) to be between 0.0 and 360.0. Such an
assignment does not count as an "action," and its effect will not be visible on the
computer screen until the next action occurs.
The move expressionnone).
The moveto expression_1, expression_2move. If both expressions are in the range
0.0 to 100.0, the bug will be visible in the window. This action changes the
bug's x and y variables,
but does not change the angle the bug is facing.
As with angle, the bug's x and y variables
can be set with assignment statements, but this will not be visible on
the screen until the next action occurs.
Finally, the line x1, y1, x2, y2none, nothing is drawn.) This action really doesn't fit
the "bugs
theme," but
is provided simply as a convenience to the programmer.
In the Bugs language, every statement and every variable declaration goes on a
separate line. An open brace, {, always ends a line; a close brace,
}, always goes on a line by itself. switch statements
must be laid out as in the example. Blank lines, and lines containing only
comments, are ignored.
Comments are as in Java.
Good indentation is recommended, but not required by the language.
Execution of a Bugs program proceeds as follows:
First, the initialization block of each bug is executed. Normally, this code should at least set the position and angle of the bug on the screen. If these variables (x,y, andangleare not set, they default to0.0. Initialization blocks are executed once and once only. With multiple bugs, there is no guarantee in which order the initialization blocks will be executed.
After initialization, the program goes into an infinite loop, as follows:
The "main program" part of each bug is executed, up to and including an action command. Upon reaching an action command, the bug pauses execution until every other bug has also executed one action command. At that point, all the various actions are reflected on the screen, and there is a brief pause (a fraction of a second).
Once the new screen image has been displayed, each bug continues from where it left off, and again computation continues until all bugs have performed another action.
Although there is no guarantee that the bugs will execute in any particular order, the programmer can rely on their being executed in the same order during each execution cycle.
Thus, all bugs will appear to the user to be moving simultaneously. The programmer can keep the bugs "in sync" by careful attention to when actions are performed.