Language Specification for Cycle
Cycle is a simple Java (or C/C++) un-typed programming language for
producing programs for Cybot from Ultimate Real Robots. It is a direct
alternative to using the graphical programming "language" supplied on the CDs
and the compiler produces .03p files compatible with Programmer 03
on CD2.
Keywords
There are a number of reserved words in Cycle, which may not be used as
identifiers. These are:
block
break
case
class
const
default
|
do
else
false
for
if
include
|
input
output
proc
process
return
switch
|
true
while
|
Operators
There are also a number of operators in Cycle. These are:
+
-
*
/
|
==
!=
>
>=
<
<=
|
+=
-=
*=
/=
++
--
|
Comments
There are two types of comment in Cycle. One is the line (or C++ style)
comment, which begins with a double forward slash and ends at the next
newline:
// This is a line comment.
The other is the block (or C style) comment. This starts with a forward
slash followed by an asterisk and ends with an asterisk followed by a forward
slash. It can contain newlines:
/* This
is a block
comment. */
Numbers
Numbers are used as values to be sent to outputs and
processes or to be read from inputs. They are also use in the
manipulation of variables using assignments and conditions. A
number may be any positive or negative integer, including zero. There is no
defined maximum or minimum number, but it is sensible to keep in the range
-127 to 127. Numbers can be defined with a regular expression:
"-"[0-9]+ or [0-9]+
Strings
Strings are used as values to be sent to blocks in member
definitions and also as filenames to include directives. A string
consists of a double-quote, followed by any sequence of characters until
another double-quote is encountered. There is no escaping of characters in
Cycle, so it is not possible to include a double-quote as part of a string.
Strings may also not contain newlines. Strings can be defined with a regular
expression:
\"[^\"\n]\"
The value of a string is the characters enclosed in the quotes (not
including the quotes themselves).
Identifiers
Identifiers are used to give names to variables,
procedures, classes and instances. Identifiers
consist of a letter or an underscore followed by any number of letters,
underscores or numbers. As a regular expression, this would be:
[A-Za-z_][A-Za-z0-9_]*
Identifiers are case-sensitive i.e Foo and
foo are not the same.
Types of Arguments
Classes and members in Cycle may take parameters. The
formal declaration of the parameters a class and member
may take is called an argument list. This is simply a list of
identifiers, one for each parameter and the identifiers
is used as a name through which the value of the parameter can be
accessed. As Cycle is un-typed, you can pass either a number or a string as a
parameter and Cycle will accept it. There are however no attempts to
convert numbers to strings or vice versa - this is simply not necessary as the
values out output verbatim.
Include Directive
An include is simply the keyword include followed by a
string and a semicolon ('; '):
class string ;
All the definitions in the file named by the string are included as
through they were defined at the point of the include directive. This
is the only way to re-used code between files in Cycle.
Classes
Classes declare an aspect of a robot. A class starts with the
class keyword and if followed by an identifier (the
class name). Classes must have a name which has not already been
defined as either another class or as an instance or
procedure. By convention class names start with an uppercase
letter e.g. Sonar . This is then followed by an optional
argument list, in which by convention each class argument name
begins with a lowercase letter. This is then followed by the class
body.
A class body consists of an opening brace ('{') followed by a
sequence of member definitions, followed by a closing brace ('}').
class identifier [ (
identifier [ , identifier ... ] ) ]
{
member-list
}
A member is one of: an input; an output; a
process; or a constant. These consist of the one of the keywords
input , output , process or
const , followed by an identifier (the member name).
constants are just an equals (= ) followed by a value
(either a number or a string) and the others all have a (possibly empty)
argument list, followed by a colon (: ) and a
member-definition. All are terminated by a semicolon (; ).
-
input [ { min
, max } ] identifier (
[identifier [ , identifier ... ] ] )
: member-definition ;
-
output identifier (
[identifier [ , identifier ... ] ] )
: member-definition ;
-
process identifier (
[identifier [ , identifier ... ] ] )
: member-definition ;
-
const identifier =
number-or-string ;
Member names must be unique within the class that they are
declared. Thus, a constant may not have the same name as an
input member for example. By convention, member names begin with
a lowercase letter e.g. getRange , except for constants
which are written entirely in uppercase e.g. OUTOFRANGE . Also by
convention member argument names begin with a lowercase letter.
A member-definition is simply the keyword block followed
by a list of block-parameters in parenthesis. The first parameter
is always a number, which identifies the kind of block being generated (see
Appendix A).
block ( number
[ , block-parameter [ ,
block-parameter ... ] ] )
Each block-parameter may be:
- a number
- a string
- the identifier of a class or member argument
Instances
Instances declare as specific use of a class. Instances
consist of the identifier of a previously declared class,
followed by an identifier (the instance name) and an optional
parameter list in parenthesis and a semicolon (; ). The
parameter list must match the argument list in the class
declaration.
identifier identifier [ (
parameter-expression [ , parameter-expression
... ] ) ] ;
The name of the instance must not have already been defined as either
another instance or as a class or procedure. By
convention instance names start with a lowercase letter e.g.
sonar .
Procedures
Procedures group a set of statements into a convenient re-usable
block. All programs must contain at least one procedure called
main . A procedure starts with the keyword
proc followed by an identifier (the procedure name)
and a list of zero or more argument identifiers in parenthesis. All
this is followed by a procedure body in braces, which is simply a list
of statements. A procedure must not have the same name as either
another procedure, a class or an instance. By
convention, all procedure names start with a lowercase letter e.g.
main .
proc identifier (
identifier [ , identifier ... ] )
{
statement-list
}
Statements
A statement is one of the following (each defined separately):
- if or if/else statement
- while or do/while loop statement
- for loop statement
- switch statement
- assignment statement
- member-call statement
- procedure-call statement (not yet implemented)
As a rule, statements are either terminated by a semicolon
('; ') or finish with a block delimited by braces ('{ '
and '} '). Unlike C/C++ or Java, braces are mandatory on
if, if/else, while, do/while and for
statements. This eliminates what is known as "the dangling else
problem" and makes the code easier to read (and parse).
If And If/Else Statement
An if statement consists of the keyword if followed by a
condition-expression in parenthesis, followed by an opening brace
('{ '), as list of statements and a closing brace
('{ ').
if (
condition-expression )
{
statement-list
}
An if/else statement is exactly the same as an if statement, but
contains an else part:
if (
condition-expression )
{
statement-list
}
else
{
statement-list
}
The execution of an if statement is such that if the
condition-express (defined later) evaluates to true, then the part in
braces following the if is executed, and execution resumes after
the closing brace of the if. If the condition-expression
evaluates to false, then the block following the if is skipped,
and execution once again resumes after the closing brace.
The execution of an if/else statement is exactly the same if the
condition-expression evaluates to true, but if
condition-expression evaluates to false, the statements in the block
following the else are executed. In both cases, the execution
resumes after the closing brace of the block for the else part.
There is also another form of if/else, which is allowed, but which
does not conform to the rule that the else part must be surrounded by braces.
This allows another if to be tagged onto the end to provide the
else if functionality found in many languages:
if (
condition-expression )
{
statement-list
}
else if( condition-expression
)
{
statement-list
}
While and Do/While Loop Statement
A while statement consists of the keyword while ,
followed by a condition-expression in parenthesis and list of
statements in braces:
while (
condition-expression )
{
statement-list
}
A do/while statement consists of the keyword do ,
followed by a list of statements in braces, then the keyword
while and a condition-expression in parenthesis, terminated
by a semicolon ('; '):
do
{
statement-list
} while (
condition-expression ) ;
Both forms of while loop work in exactly the same, and differ only in
the point at which their condition-expression is evaluated. In the case
of the while loop, the condition-expression is evaluated at the
start of every repetition of the loop, so the code in the block can be executed
zero or more times. With the do/while loop, the
condition-expression is evaluated at the end, so the code in the block
can be executed one or more times. In both cases the loop carries on repeating
whilst the condition-expression is true.
For Loop Statement
The for statement is similar to the while statement in that it is
written with the condition at the top (and can therefore be executed zero or
more times), followed by a block of statements which are repeated whilst the
condition-expression remains true. The only difference between a
for statement and a while statement is that a for
statement also has initial assignment and increment assignment
statements within the parentheses of the for :
for (
assignment-statement ; condition-expression
; assignment-statement )
{
statement-list
}
Switch Statement
A switch statement might look quite complicated, but it is really just
a multi-pronged if/else statement, which allows several values to be
checked at once. It consists of the keyword switch followed in
parenthesis by a member-call-expression, which is simply a call to an
input member, using the same syntax as a member-call statement,
but without the semicolon ('; '). This then followed, in braces by
a list of case clauses.
Each case clause is one or more occurrences of either the keyword
case followed by a constant-expression (defined later) and
a colon (': ') or the keyword default followed by a
colon (': '). These are then followed by an optional list of
statements and the mandatory break keyword, which is always
followed by a semicolon ('; '):
switch (
member-call-expression )
{
case constant-expression
:
[ case constant-expression
:
... ]
[ statement-list ]
break
;
[ case constant-expression
:
[ statement-list ]
break
;
... ]
[ default :
[ statement-list ]
break
; ]
}
Assignment Statement
An assignment statement consists of an identifier, an operator
symbol, e.g. equals ('= '), usually followed by an
arithmetic-expression. The statement is always terminated by a
semicolon ('; '). Therefore an assignment statement may be
one of:
- identifier
=
arithmetic-expression ;
- identifier
+=
arithmetic-expression ;
- identifier
-=
arithmetic-expression ;
- ; identifier
*=
arithmetic-expression ;
- identifier
/=
arithmetic-expression ;
- identifier
++ ;
- identifier
-- ;
Assignment statements work only on Cybot's internal variables,
which can be thought of as a set of boxes labeled 'a' to 'l' (lowercase 'L').
Assignments can be simple, where one box is given a copy the value that is in
another box, or they can use simple arithmetic. This means that the only valid
identifiers are the letters 'a' to 'l'.
To keep things simple, an arithmetic-expression may consist of at
most two simple values. An arithmetic-expression can be any of the
following:
- number-expression
- number-expression
+ number-expression
- number-expression
- number-expression
- number-expression
* number-expression
- number-expression
/ number-expression
Member-Call Statement
A member-call statement is the usage of an output or process
member of a class via an instance. It consists of an
instance identifier followed by a dot ('. ') and the
identifier of a member. The member must have been defined
as either an output or a process. This is then followed by a
list of zero or more parameters in parentheses. The whole statement is
terminated by a semicolon ('; ').:
identifier . identifier
( [ parameter-expression [ ,
parameter-expression ... ]) ;
Procedure-Call Statement
NOT IMPLEMENTED.
Parameter Expression
A number-expression is simply either a literal number e.g.
2 or a constant member which evaluates to a number:
- number
- string
- identifier
. identifier
In the latter case, the first identifier is a class name and
the second identifier is the name of a constant member of that
class which can be defined to be either a string or a number
Constant Expression
A number-expression is simply either a literal number e.g.
2 or a constant member which evaluates to a number:
- number
- identifier
. identifier
In the latter case, the first identifier is a class name and
the second identifier is the name of a constant member of that
class which must be defined to be a number
Condition Expression
A condition-expression consists of a comparison between two
number-expressions. It is written as a number-expression
followed by a comparison operator, followed by another
number-expression. It is therefore one of:
- number-expression
== number-expression
- number-expression
!= number-expression
- number-expression
< number-expression
- number-expression
<= number-expression
- number-expression
> number-expression
- number-expression
>= number-expression
Number Expression
A number-expression can be either of the following:
In the latter case the identifier is a letters between 'a' and 'l'.
Appendix A - Block Types
The graphical programming language for Cybot uses the following block types in
its .03p files. Each block has an id number indicating its type.
These values are used as the first parameter to block in
member-definitions in Cycle.:
0 = Start
1 = If/Then/Else
2 = Arithmetic
3 = Delay Timer
4 = Sound
5 = Motors
|
6 = Sonar Sensor
7 = Jump
8 = Land
9 = Antennae Lights
10 = Light Sensor
11 = Line Testing (not on CD2)
|
12 = undefined
13 = undefined
14 = Subroutine (not on CD2)
15 = Stop
16 = Return (not on CD2)
|
For more information see the Basic Tutorial and
the Advanced Tutorial.
|