Also, the following three operator characters
cannot be used to make operator strings:
":" Reserved for declarations
"::"
Reserved for scope selectors
".."
A range token.
When an operator character is also a
separator, it should not be used in combination with other separator
characters. For example:
;=
cannot be an operator string because
";" is a separator.
These are some function names that use
operator strings:
++
<==>
Some operator
names may contain more than one operator string, such as:
<* *>
Why is this? Because "<*" and
"*>" are two operator strings which constitute two parts of a name
of a function. When this function’s
name is referenced, the two parts should be written separately unless they
happen to be separators.
Therefore,
<**>
is a different
name from
because "<**>" is a single operator string
and “<*” and “*>” are two operator strings. Consider:
[ ]
These characters
are two parts of a name for an array index operator. Since they are also separators, the string:
[]
is still the same
operator and has two individual lexical elements.
Unless an operator
character is a separator, an operator string will terminate only when the next
character is not an operator character.
This requires programmers to use a white space or another kind of
separator to separate two operator strings.
For example, the expression
x--+--y
in
C++ or Java contains three operator names: "--",
"+", and "--".
ScriptV
considers "--+--" a single operator name. This encourages programmers to write clear
expressions when using ScriptV. The
above example in ScriptV would be written as
x-- +
--y
or
(x--)+(--y)
1.3.4. Literals
Literals are the fundamental representations of the value of an
object. There are four kinds of
literals:
Integer
Character
Floating-point
Number
String
In some of the following pages, the word "constant"
is used in reference to literals. A
literal constant (Integer, Character, Floating-Point, or String)
is a value that cannot be changed by any program expression. "Constant" is opposite in meaning
to "Variable." A variable is
an identifier that represents a value
in a program expression. The value a
variable represents may be changed.
1.3.4.1. Integer
Decimal integers
and hexadigital integers comprise the two kinds of integer literals used in
ScriptV. These integer literals are all
of the type Int.
A decimal integer is a sequence of the following digit
numbers:
0
1 2 3 4 5 6 7
8 9
Examples of decimal integers are:
256
00234
4560000
A hexadigital integer starts
with the characters "0x" or "0X" and contains a sequence of
the following hexadigits:
0
1 2 3 4 5 6 7
8 9 A B C
D E F a b c d
e f
Examples of hexadigit integers are:
0x45feb
0X0045ee00
0xFF00E0A0
1.3.4.2. Character
A character constant, or literal, is
one character enclosed in single quotes, as in 'c'. A character constant may or may not be preceded by the character u,
which is a Unicode prefix. A character
has the type Char.
The actual character that is represented by a character
literal depends on the character set used in the application. Character sets define a mapping method
between the characters and the integer numbers that are used to represent the
character. Most western languages can
use an 8-bit integer for the mapping because the number of characters in a
western language is usually less than 255.
Some Asian languages may contain thousands of characters and must use a
16-bit integer for coding.
In some display contexts, a character may
appear as two separated alphabetic symbols, as in 'ÎÄ'. This is
caused by displaying a character with an inappropriate font. The literal 'ÎÄ'
may represent a single Chinese character if the ChineseBig5 character set is used, or may be an invalid character
if the ANSI character set is used.
Certain non-graphic characters, as well as
the single quote ', the double quote ", the question mark ?,
and the backslash \, may be represented in ScriptV code according to
the following list:
|
Character
|
ASCII Representation
|
Escape Sequence
|
|
Null character
|
NUL
|
\0
|
|
New line
|
NL (LF)
|
\n
|
|
Horizontal tab
|
HT
|
\t
|
|
Vertical tab
|
VT
|
\v
|
|
Backspace
|
BS
|
\b
|
|
Carriage return
|
CR
|
\r
|
|
Form feed
|
FF
|
\f
|
|
Alert
|
BEL
|
\a
|
|
Backslash
|
\
|
\\
|
|
Question mark
|
?
|
\?
|
|
Single quote
|
'
|
\'
|
|
Double quote
|
"
|
\"
|
|
Hex number
|
Hh
|
\xhh
|
If the character following the backslash
does not specify a legal escape sequence, the result is undefined.
The escape \xhh consists of a backslash followed by x followed by a sequence of hexadecimal digits, and is terminated
by either another x or by a
non-hexadecimal digit.
These are some character constant examples:
'x'
'"'
'ÎÄ'
'\''
'\\'
'\x6E'
'\x6Ex'
1.3.4.3. Floating Point Literal
A floating-point literal is a number that consists of an
integer part, a fraction part begun with a decimal point, and an optional
exponent part.
The syntax of the floating-point number is
defined as following:
FloatingPointNumber:
DecimalInteger . DecimalIntegeropt Exponentoptional
FloatingSuffixoptional
DecimalIntegeroptional .
DecimalInteger Exponentopt FloatingSuffixoptional
DecimalInteger Exponent FloatingSuffixoptional
Exponent:
e
Signoptional DecimalInteger
E
Signoptional DecimalInteger
Sign:
one of
+ -
FloatingSuffix: one of
f d F
D
The subscript optional next to certain of the above words designates it as
“optional.”
Here are some examples of floating-point
number literals (also known as “floating-point constants”):
18.23
18.23f
18.46e4d
18e4
By using the suffixes of f(F)
or d
(D),
a literal can be specified as a Float or a Double. These designations represent single float or
extended double precision float, as specified by IEEE standard floating-point
formats. Floating-point literals are
double type by default.
1.3.4.4. String
A string literal consists of zero or more
characters surrounded by one or more pairs of double quotation marks.
Examples of strings are:
"this
is a string, and the next line is an empty string"
""
A string literal has a type of String.
A string literal is an array of characters
that receives an initial value of the characters that are included in the
string. When we type a string that includes alphabetic characters, the computer
will interpret these alphabetic characters as a series of numbers that
possesses a specific value. For
example, when we type the string “abcd” the computer will read something
like “41,42,43,44,0” where “41” is “a”, “42” is “b”, “43”is “c” and “44”
is “d”. The computer automatically inserts a 0 at the end of every string to represent a “null” character
terminating that particular string.
Therefore, we can say a ScriptV string
is terminated with a null character.
Adjacent string literals are joined to each other in the
order in which they appear. This is
called "string concatenation."
Characters in these contiguous strings are kept distinct. For example:
"\x01"
"edit"
contains five characters:
'\x01','e','d','i', and 't' after concatenation. It is not equivalent to '\x01edit',
which contains only three characters: '\x01ed', 'i', and 't'.
The specification of string literals that
contain embedded hexadecimal escape codes can cause unexpected results. For example, the following string is
intended to create a string that contains ASCII '4' followed by the characters
"four":
"\x04four"
However, if the computer read this string,
the actual result would start with '\x4f', which is actually an ASCII code for
the character 'O', and would end with the characters “our”. The following examples will produce the
desired result:
"\x04xfour"
"\x04"
"four"
Depending on the
character set used, characters contained in a string may occupy different
lengths of space. For instance, each
ANSI character occupies a single byte, but for Unicode character sets, each
character occupies double bytes. For
some character sets, such as ChineseBig5 and
Japanese ShiftJis, mixed single and
double bytes are used. Therefore, it is
not appropriate to use a string as a character array to get a specific
character in a given index for some specific character set. Where the character set is unknown, it is
recommended to use high-level text operations, rather than the low-level string
operations.
1.4. User-Defined
Lexical Elements
ScriptV is a language that can be
extended. Animation model developers
may program new lexical elements into their animation models and plug-ins for
the Visviva Animation Engine. For
example, the lexical element for a date may be:
11/30/98
A value for time may
use:
12:30:23
Likewise, a hypertext may use a completely
different lexical element, such as those lexical elements used in HTML.
User-defined lexical elements can allow
unlimited freedom for ScriptV programmers.
Please refer to the reference manual for further information on each
specific user-defined lexical element.
2. Classes and Objects
ScriptV is composed of a set of
class/object specification files. Each class/object specification describes a group
of objects that have common attributes, compositions, and similar functions.
Each ScriptV object is created from an
associated class. A class, therefore, is a template for objects. Like objects,
classes can contain variables and parameters, can be transferred from one place
to another as values, and can be created dynamically.
This chapter will focus on the standard
constructs that are used to define ScriptV classes. It will also reveal how objects are created from classes.
2.1. Objects
Objects
are the fundamental components of applications created from a ScriptV program.
Objects are usually models that simulate or animate conceptual or real-world
entities, and each object possesses attributes,
compositions, and functions.
2.1.1.
Object Attributes
Object attributes describe various
properties of an object, such as states, shape, image, color, style, size,
position, posture orientation, material, texture, text, sound, etc.
An object attribute has a name
and an associated value.
The value of an object attribute
represents the current property setting for the object’s attribute. For instance, a color attribute may have the
values red, blue, pink, or brown.
An image attribute can have a character string as its value that names a
file containing an image.
An attribute’s name is used to access
and manipulate that attribute. To call
someone on the telephone, we need to look up the person’s name in the phone
book. To find a certain house, we need
the name of the house’s street address.
In the same way, we must tell the ScriptV system which particular
attribute we want in order to access or assign the value of that
attribute. We do this by presenting the
name of the desired attribute. We can
use a name to get the current value of the attribute (attribute read), or to
give a new value to the object’s attribute (attribute write).
In addition, an object attribute may be a reference
to another object. For example, a frame object may have references to its
horizontal and vertical scrollbars, both of which are objects themselves. When
an object encloses other objects, it is important to make a distinction between
the value of the reference itself and the object that is designated by the
reference.
The same objects may be referred to by more
than one object reference.
2.1.2.
Object Composition
Object composition refers to the
various component objects an object may contain. These component objects are often referred to as child
objects. An object that
encloses child objects is called a parent object.
Let us consider a table. A table can
contain four legs, named leg1, leg2, leg3, and leg4. These four legs are child objects of the
object table. Child objects are referred by their names
through their parent object. For
instance, if we refer to the leg named leg1,
we must indicate which table’s leg1.
A child object may not be created before its parent
object, nor can it exist after its parent is destroyed. This means that when we delete our table,
its four legs will also be deleted.
When an enclosing object is deleted, the Visviva Animation Engine will
delete all of its child objects automatically.
Noting the distinction between child objects and object
attributes may be confusing at first.
As we mentioned in the previous section, an object attribute may be a reference
to another object. This another object that an attribute refers
to may or may not be a child object, as another
might be an object belonging to another entity. When an object is deleted, the object does not delete the objects
that refer to its attributes unless these attributes refer to child
objects. For instance, if the table
object has attributes referring to six chairs, none of the chairs will be
deleted when the table is deleted.
2.1.3.
Object Functions
An object function describes a sequence
of actions that an object can perform.
An object may have a number of different functions. Each function is assigned a different
name. When we want an object to do
something, all we have to do is assign the object some function names that
describe a sequence of actions we would like the object to perform. The process of assigning the functions of an
object is referred to as a function call.
ScriptV functions are versatile and
flexible, and can be more than a sequence of actions. Suppose we want a function that makes an object move to a new
position. Should we write different move functions for each different
position the object could possibly move to (say, move_to_position1, move_to_position2)? We could, but this would not be practical.
For this reason, function input
parameters are used as an efficient method of assigning variations to
actions. Function input parameters are
an arrangement of variables and parameters that are used to implement a
function. Like an object attribute, a
function input parameter will have a name and a value. Thus the move
function may have an input parameter named position
that indicates where an object is to be moved.
When a function input parameter is
described in a function definition,
the actual value associated with the input parameter is not assigned. Instead, the actual value of a function
input parameter must be assigned at the time the function is called. The function asks for the name of the
parameter at the time of the function execution in order to access its actual
value. Therefore, to enact a move function of a ScriptV object, a
position value, say (3,4), must be assigned to the input parameter position. When the object executes the
move function, it will ask for the input parameter position to get the position value, i.e. (3,4), and move to the
given position.
A function
output call compels an object to return a certain value. For instance, we may request an object to
calculate its position in screen coordinates and expect a return of the screen
coordinates. Asking a function to
return an output requires the use of function output parameters. An output function parameter names the type of
value that will be returned when the function is performed.
2.1.4.
Object Storage
An object storage describes how
object values are stored.
Object values are defined by different
application-dependent storage classes. A typical usage of an object storage is
as a persistent storage. A persistent storage keeps a persistent copy of the
value of an object so that the value of the object can outlive the
application. That is to say, when an
application is quit, or even when the computer is shut down, the object value
will not be lost. Instead, the next
time the application is started, the object value will be loaded to reflect the
last change of the object.
2.2. Classes
A class is usually described as a
template from which objects are actually made.
A class will have a set of declarations that will specify an object’s
attributes, composition, and functions.
Class declarations will also specify an object’s initializer. Class declarations are contained in the
class body and are surrounded by a pair of curly brackets or begin-end brackets.
We will start our talk concerning Class by first describing the declaration
of attributes. Then we’ll move to
object creation, child objects, and the declaration of functions.
2.2.1.
Attribute Declaration
The attributes of an object are described
by a class's attribute declaration. An attribute
declaration gives the name and the type of an attribute and therefore
regulates the range of the values that the attribute can have. An attribute's name must be a valid
identifier. Two attributes that use the
same identifier cannot be declared in the same class body. A colon is used between the name of the
attribute and the type of the attribute.
Consider:
class Rect // “Rectangle”
{
a: Float;
b: Float;
roundness: Float;
}
The Rect
class above contains three attribute declarations, named a, b, and roundness. a, b, and roundness
respectively stand for the width, the height, and the roundness at the corners
of a rectangle. An object created from
this Rect class will have the three
attributes a, b, and roundness declared
by Rect, and the value of each
attribute may be assigned in the object body as a specific floating-point
number.
Bear in mind that object attributes are
meaningful only at the time of object creation. A class is simply a template, and an attribute defined in a class
can only be assigned a real value through objects. Therefore, we can only assign a value to an attribute through an
object that is created from a class.
When an object named r is created from the Rect
class, its three attributes, a, b, and roundness, may be accessed by an expression that combines the
object name and the attribute name with a dot:
r.a = 2.0; //
assigns the attribute a of r a value of 2.0
Attributes that have the same type may be combined into a single
attribute declaration. For instance,
the attributes a, b, and roundness all have the type Float,
and may be combined into a single attribute declaration. As shown in the following example, commas
will separate the attribute names:
class Rect
{
a, b, roundness: Float;
}
A class can specify the default value(s) of
an attribute. If the value of an
attribute is not assigned at the time of object creation, this undefined
attribute will receive the default value defined in the class attribute
declaration. A default value is
specified in the initial part of an attribute declaration. It is introduced by a “=” character, and it
is followed by an expression that gives a value the attribute can accept. For instance,
class Rect
{
a: Float
=1.0;
b: Float
=1.0;
roundness: Float
=0;
}
Here, the default values for the attributes
a and b are set to 1.0, and roundness
is set to 0. Since a and b are set to the
same value, the above example can also be written in the following format:
class Rect
{
a, b: Float =0.5;
roundness: Float =0;
}
Attribute values must be decided at the
time of object creation. If this is not
the case, the attribute value will be undefined.
As we mentioned in the beginning of this
chapter, classes are objects themselves in ScriptV. In addition, classes themselves can be attributes of an object,
which means that an attribute can request a class
as a value instead of an object. As an example, consider the item_type attribute that is declared in
the class Dictionary:
class
Dictionary
{
item_type: Type of Object;
}
In the attribute declaration item_type, the expression Type of indicates that this declaration
is a class attribute. This class
attribute requests as its value any subclass of the class Object. When we create a
subclass of Dictionary called BookCategory, we can assign a value to
the class attribute variable of NamedObject:
class
BookCategory is Dictionary
{
item_type = Book;
}
where Book
is a class, which is a subclass of Object.
ScriptV classes are stored as files. To
refer to a ScriptV class file in a specific directory, the path may be defined
by the use of the keyword "at". The path description
is written in the standard format used in a file path.
For example, to create an object called detail_frame from the class Classic1 when the class file for Classic1 is located in a different file
directory than detail_frame, the path
to Classic1's file directory would be
described inside a pair of quotation marks following the keyword at:
object
detail_frame: Classic1
at
"http://www.visviva.com/scriptv/templates/class/frame"
See 4.2.2.,
Simple Object Creation, for more information regarding the location of file
directories.
2.2.2.
Simple Object Creation
To make an object of a class, we must first
give the name of the object, a colon, and the name of the class from which the
object is created:
object
r: Rect;
Here the object r is created from the Rect
class.
Please note that instead of a colon between
the object name and the class name, the keyword is may be substituted. For
example:
object
r is Rect;
A user may assign his or her own attribute
values to the object r in an attribute
initialization. For example:
object
r: Rect
begin
a =2.0;
b =1.0;
roundness =0.3;
end
Please note that we use the begin-end
brackets here instead of a pair of curly brackets. There is no semantic difference between these two demarcations,
and either may be used depending on personal aesthetics.
As previously mentioned, each ScriptV
object is created from a class. The information that describes each class is
always stored in a Class File, and
Class files are usually located in four different places:
1. A
class file may be located in the same directory as the object file that refers
to it.
2. A
class file may be located in a sub-directory of an object file. This
sub-directory must be entitled "class".
3. A
class file may be located in the system's class directory. This is the default
directory for the Visviva Animation Engine.
4. A
class file may be a pre-set class in the Visviva Animation Engine. These
pre-set ScriptV classes are described in the Graphic Components Reference for
the Visviva Animation Engine.
The
class file an object refers to should have the exact same name as the
class. A specific path name
(../../CLASS) is not required for built-in classes, classes in the same
directory, or classes in the system’s directory. The only time that a path specification is required is when
referring to a class that is not in any of these four locations.
Additional aspects of object creation will
be discussed later.
2.2.3. Child
Objects
Child objects are declared directly in the
class body. A nested structure for
child objects intuitively reflects the architectural hierarchy of an object’s
composition. Consider the following
example:
class
TwinRect
{
object r1: Rect;
object
r2: Rect;
}
The TwinRect
class defines a graphical object containing two rectangles: r1 and r2. The width, height, and
roundness of r1 and r2 are not given in the TwinRect class. Instead, the width, height and roundness of r1 and r2 are initialized, or assigned, when we create an object from the TwinRect class. The attributes of r1 and r2 are initialized
by the placement of a "dot" between each object and its attribute:
object my_graph: TwinRect
{
r1.a =
4.0;
r1.b = 2.0;
r2.a = 3.0;
r2.b = 1.0;
}
Please note that dots may be used to assign
the value of an attribute to enclosing objects.
ScriptV also provides the with initialization statement to
eliminate the necessity of repeatedly typing the name of an object. Here, the rectangle r1 is assigned values of a
= 4.0, b = 2.0, and roundness = 0.2:
object my_graph: TwinRect
{
with
(r1)
{
a = 4.0;
b = 2.0;
roundness = 0.2;
}
}
A child object may contain other child
objects:
class CompositeGraph
{
object
more_rect: TwinRect
{
object
r3: Rect;
}
}
This CompositeGraph
object, when created, will contain a child object, called more_rect. The child object
more_rect will contain three child
objects: r1, and r2, as defined in the TwinRect
class, and additionally, r3, which is
also a rectangle.
The with
initialization statement can also be used to add more child objects into an
existing child object:
object my_composite_graph: CompositeGraph
{
with
(more_rect)
{
object
r4: Rect;
object
r5: Rect;
object
r6: Rect;
}
}
More rectangles, r4, r5, and r6 are added into the child object more_rect.
2.2.4.
Function Declaration
The function of an object is specified by a
function declaration in the class
body. Functions may take input parameters and can return outputs.
A function
declaration begins with the keyword func,
followed by the function name. The
function declaration may also include input parameters, output specifications,
and a function body. The function body
contains a sequence of statements that describe the actions associated with the
function.
Here’s an example of a function
declaration:
class Rect
{
a: Float;
b: Float;
roundness: Float;
func Resize (new_a, new_b: Float)
{
a =
new_a;
b =
new_b;
}
func GetRoundness ():Float
{
return
roundness;
}
}
The rectangle object that this class template
creates will have two functions: Resize
and GetRoundness.
The function Resize has two input parameters: new_a and new_b. new_a
and new_b give the new size of the
rectangle; both of these inputs are of the type Float. The input
parameters are contained in a pair of parentheses.
In a function declaration, an input
parameter is specified with the name of the parameter, a colon, and then the
type of the input parameter.
The body
of a function consists of a list of statements contained within a pair of curly brackets, or begin-end brackets. Semicolons separate each statement in a function body. There are two assignment statements listed in the body
of the above Resize function. These
statements simply assign a new width and height to the a and b attributes.
The function GetRoundness is an output function, and does not take any input
parameters. For this reason, the pair
of parentheses for the input parameters is empty. The function has an output of the type of float. In this function
body, GetRoundness simply returns the
value of the roundness attribute as
its output.
If a function does not have any input
parameters, a pair of empty input parameter parentheses must be presented, as
seen in the declaration of the function GetRoundness.
Like object attributes, an object function
can only be called through an object created by a class. A function call is written in an expression
that combines the object name, the function name, and if required, a number of
input parameters.
The declaration
func Resize (new_a, new_b: Float)
in the class Rect states that its Resize
function requires two floating-point numbers as input. If we assume that an object r is created from the class Rect, we can call r’s Resize function with
input parameters by combining the object name and the function name with a dot,
followed by a pair of parentheses with the actual values for the input
parameters:
r.Resize(3.5, 2.2);
Here we've written a function call that
makes the rectangle r resize itself
to a new size of a=3.5 and b=2.2.
The floating-point numbers given in this expression are the object's
actual parameters for the input values expected by the function Resize.
The order in which the actual parameters are given must be the same
order in which the input parameters are declared.
Input parameters and output type
specifications are the signatures of a function. A function's signature defines an interface
regulating what type of inputs should be given and what type of output is
expected.
When calling a function, it isn’t necessary
to define the actual values for every input parameter because a default value
may be associated with the input parameters of a class function declaration. A
default value is given in an expression that follows the parameter type and the
character “=”. Consider:
class Frame
{
func SetStyle
(
border_style: BorderStyle = NoBorder;
background_style: BackGroundStyle = SolidBackground;
title_style: TitleStyle = NoTitle;
draw_style: DrawStyle = ();
layout: Layout = ()
)
{
// set
various frame styles here
}
}
The SetStyle
function of this Frame class contains
various style input parameters, each possessing a default value. Default values were assigned to the border_style, background_style, title_style,
draw_style and layout styles.
During a function call, if the actual
parameters are not assigned in the order in which they were declared in the
class, the name of the parameters must be explicitly given. Therefore, assuming that f is a Frame object, we can have:
f.SetStyle
( border_style = Extrude,
layout =
(Vertical, AttachTop, FloatingX)
);
A note to C++ and Java programmers: in
ScriptV, either commas or semicolons may separate input parameters. In C++ and Java, only commas can separate
input parameters. Additionally, in
ScriptV, input parameters that can have default values are not necessarily
limited to those at the end of the parameter list.
2.2.5.
Creating Objects Using Specific Storage
To create an object using a specific
storage class, the object declaration may be prefixed with the name of the
storage class. The available storage
classes provided by ScriptV are implementation-dependent. For example, if an implementation provides a
persistent class for persistent storage, then a persistent object may be
declared in the following syntax:
persistent object name: SomeClass { }
2.3. Subclass
2.3.1. The
Concept of Subclass
As we discussed in “Lexical Conventions”,
all classes are not designed individually.
Very often, situations will arise where a new class can be designed
based on the characteristics of a previously existing class. In chapter 1, for example, we designed a new
IconListFrame subclass from the Frame class. In this chapter we'll discuss additional concepts of subclass
construction.
Consider the classification hierarchy of
animals: suppose that we had already defined a fish class and we needed to describe a new class of a specific fish
species, such as salmon. To describe what a salmon is, we can state, "A
salmon is a fish," and subsequently name all of the specific
characteristics possessed by salmon. We
have already defined the class fish,
so the statement "A salmon is a fish"
sets us free to name a set of new characteristics specific to the class salmon without having to describe again what
the characteristics of the class fish are. By beginning our description of salmon in
this manner, we avoid repeating the common attributes of the fish and salmon classes.
ScriptV subclasses are described in this
same ordinary manner:
class Salmon is Fish
{
// attributes and functions specific to salmons
}
Here, the class Salmon is specified as a distinct class of Fish. Though a salmon has
all of the attributes that were described in the Fish class, we don’t need to repeat these in the Salmon class body. Instead, all we need to describe here are
the attributes specific only to salmon.
We can therefore call Salmon a subclass of Fish.
A subclass of a class designates a subset of the
objects in a class by providing the subset with additional specific attributes
and functions.
Superclass
is the counter concept of subclass. Fish,
then, is a superclass of Salmon.
All attributes and functions that are described in a superclass will
appear in its subclass. This phenomenon
is referred to as inheritance. The Salmon
class inherits all the attributes
and functions defined in its superclass Fish.
A note in syntax: like C++ and Java, a
class declaration in ScriptV can also use a colon to introduce its superclass:
class Salmon: Fish
2.3.2.
Specialization
As discussed in the previous section, a subclass adds new attributes, child
objects, and functions to the characteristics that were inherited from its superclass.
Frequently, it is the case that a subclass
may need to change certain things inherited from its superclass. We call this change specialization.
2.3.2.1. Attribute Value Reassignment
A subclass
of a class designates a subset of
the objects in a class by providing the subset with additional specific
attributes and functions. At certain
times, however, an attribute that is declared by a superclass might not be
assigned a specific value during the definition of the superclass. In cases like these, the actual value of an
attribute that is still undeclared at the time of the superclass’s definition may
be assigned in a subclass.
In addition, when a superclass assigns a
value to an attribute, it is not uncommon that a subclass might need a
different value for the same attribute.
Consider:
class Scale
{
style:
ScaleStyle = ();
}
class HorizontalScale is Scale
{
style =
(Horizontal);
}
The attribute style in the class Scale
is set to empty, which means “no
particular style assumed.” In the
subclass HorizontalScale, the style attribute is reassigned to (Horizontal), which means that objects
instantiated from HorizontalScale will
have scale thumbs that will travel horizontally.
HorizontalScale
is a subclass of Scale (and Scale is a superclass of
HorizontalScale), and so HorizontalScale may reassign the style value
declared in its superclass.
Therefore, a subclass can assign or
reassign values to attributes that were previously defined in a superclass by
using a manner similar to the attribute value assignment discussed in 4.2.2., Simple Object Creation. A subclass may also reassign the value of
child object attributes defined in a superclass. For instance:
class
ImageScale is Scale
{
object background is Image { pixmap =
“slot.bmp”; }
object thumb is Image { pixmap =
“knob.bmp”; }
}
class
WoodScale is ImageScale
{
background.pixmap = “woodslot.bmp”;
thumb.pixmap = “woodknob.bmp”;
}
Here, the pixmap attributes of the background object and the thumb object are
changed in the subclass WoodScale.
By using the with initialization statement, attributes, components, or functions
may be added to child objects defined in a superclass:
class
VariantThumbImageScale is Scale
{
style =
(VariantThumb);
object thumb is Frame;
}
class
MyVariantThumbScale is VariantThumbImageScale
{
with (thumb)
{
object thumb_head: Image ...
object thumb_tail: Image ...
object thumb_body: Image ...
}
}
2.3.2.2. Function Override
A subclass can also change a function
defined in its superclass. This process
is called function override. Consider:
class
QuitButton is Button
{
func
OnButtonActivated () { Quit(); };
}
class
GracefulQuitButton is QuitButton
{
func
OnButtonActivated ()
{
root.Create
(
class
Dialogue
{
/* quit dialogue description */
}
);
};
}
The subclass GracefulQuitButton rewrote the OnButtonActivated
function that was defined in the class QuitButton. The GracefulQuitButton
will now create a quit dialogue box as described in the class Dialogue.
A note to C++ and Java programmers: ScriptV
is a dynamic language. It does not need
to check the signature in order to determine whether a function is overridden
in a subclass. The override is always assumed, even when the signature is
different. Consider the following two
classes:
class
A
{
func foo
(a: Int);
}
class
B is A
{
func foo
(a: String);
}
Assume x
to be a variable of the type A:
x: A;
and x currently refers to an object created
from the class B. Calling the function foo:
x.foo(“abcdefg”);
is valid in ScriptV, and the foo function defined in the class B will
be performed. However, the above function
call is invalid in C++ or Java.
2.4.
Initialization
In an animation title, we often need active
objects that can perform a sequence of actions immediately after they are
created. To do this, ScriptV offers the
initializer. For example, with an initializer, a fish
created in a ScriptV aquarium may begin swimming immediately upon its
appearance in the aquarium.
An initializer contains a sequence of
actions for an object that are performed at the very beginning of the object’s
instantiation. Like a function, an
initializer may have input parameters that describe a set of input arguments
that are necessary to implement the initial values of an object. Some possible initializer arguments might
include a command to begin an object's animation, or to set the text, extrusion
length and character spacing of a 3D text object. The actual value associated with an initializer’s input parameter is given at the time the object is
created. Initializers are constructed in very much the same manner as
functions. Unlike a function, however, an initializer cannot
have an output.
2.4.1.
Initializer Declaration
An initializer declaration is introduced by
the keyword enter. An initializer declaration may include input
parameters and a sequence of statements that describe the actions associated
with the initializer. Consider:
class
Fish: AnimateFrame
{
enter
(x, y: Float)
{
MoveTo
(x,y);
animation.Start();
}
}
The class Fish declares an initializer that has two floating-point number
input parameters: x, and y.
To create an object out of the class Fish,
the actual values required by the input arguments must be provided:
object
a_fish is Fish(Left, Center);
When instantiated, the object a_fish will move to the position given
by the input (that is, in the center of the left side), and begin its animation
from there.
An initializer can be provided at the time
of class or object creation. When an
object initializer is declared, the initializer cannot contain any input
parameters. Therefore,
object
fish: AnimateFrame
{
enter
(x, y: Float)
{
MoveTo
(x,y);
animation.Start();
}
}
is not a correct ScriptV code since the
initializer contains two inputs.
Instead, the initializer should be written:
object
fish: AnimateFrame
{
enter ()
{
MoveTo
(3.3, 2.5);
animation.Start();
}
}
2.4.2.
Initializer in Superclass
Initializers that are defined in
superclasses are not inherited automatically by their subclasses for the
following reasons:
• Subclasses
normally have a specific way to deal with member objects that have already been
defined in their superclass.
• Superclasses
may require different input parameters; this will make it unclear which
parameter is to be inherited.
However, the initializer of a subclass may
need to rely internally on an initializer that was defined in its
superclass. There are two methods of
accomplishing this in ScriptV:
1. Call
the initializer of the superclass directly, or
2. Have
the initializer be defined as an automatic
initializer in the superclass.
In the first method, a superclass initializer
is selected by presenting a list of actual parameters that match the input
parameters of the initializer. For
example:
class
Point
{
x, y: Float;
enter (a, b: Float)
{
x =a; y =b;
}
}
class
ColoredPoint is Point
{
c: Color;
enter (a, b: Float; color:Color)
{
super(a,b);
c = color;
}
}
The initializer of ColoredPoint calls the initializer of Point by the expression super(a,b). The keyword super in ScriptV denotes the superclass.
In the second method, the superclass will
have an automatic initializer. An
automatic initializer cannot be called directly, as in the first method. Instead, a superclass's automatic
initializer it is always called involuntarily by its subclass. No input parameters can be declared for an
automatic initializer. Consider:
class
Fish: AnimateFrame
{
auto enter ()
{
animation.Start();
}
}
class
NoisyFish: Fish
{
enter ()
{
PlaySound(“noise.wav”);
}
}
The superclass Fish has an automatic initializer that is
inherited and called automatically by its subclass NoisyFish. In other words,
when an object of the NoisyFish class
is created, the initializer that was defined in Fish is automatically called first, and the initializer defined in
the NoisyFish is called second. An
automatic initializer that is defined in a superclass must be called before an
initializer defined in its subclass can be called.
2.4.3.
Initialization Order
When we create an object from a class, we
must perform the following initialization work:
1) Assign (initialize) the values of the
attributes, as specified in the attribute declaration of the class
2) Call the initializer
In the initialization order, attribute
initializations are first and initializers are second.
In attribute initialization, the attributes
that were declared in the superclass are initialized first and then the
attributes that were declared in the subclass are initialized. Within the same class, attributes are
initialized in the order of their declaration.
When calling automatic initializers, the
automatic initializers defined in the super class are called first, and next
the automatic initializers defined in the subclass are called.
When calling object initializers, the
initializers of the child objects are called first. Then, initializers of the enclosing object are called. An
initializer, if present, is called after an object’s attribute
initialization. If there is an
automatic initializer defined in the superclass, the automatic initializer is
called first. Then the initializer of
the object, if any, may be called.
3. Basic Types and Class Constructs
ScriptV is a typed language, which means
that every attribute and every parameter has an associated class. A class is a template from which objects are
created. A class also indicates the
operations it supports for the values it holds.
In ScriptV, there are a number of
predefined basic classes for commonly used data types. These data types are Int, Char, Float, Double, Bool, and String, and these types can be used
directly in attribute or parameter declarations. For example, the following declarations:
extrusion: Float;
angle: Float;
provide two attributes, and both are in the
Float type. The Float type designation
limits the values of these attributes to floating-point numbers.
ScriptV also provides a number of basic class constructs for commonly used data
structures. Class constructs provide
users with a simple way to define various specific types and complex
operations. The built-in class
constructs are enum, set, stream,
array, tuple, and super.
A class construct can be used to define a
class. For example,
class
Bool is enum (False, True);
defines a class Bool, which is an enumeration type (explained later) containing two
possible values: True or False.
We can also use a class construct to
specify the type of an attribute or parameter directly:
signal: enum (High, Low);
3.1. Integer
The type of integer values, as described in 3.2.4.1
Integers, is Int.
ScriptV provides a number of operators that can act on
integer values:
Comparison Operators ==, !=,
>, <
, >=, and <=.
When Comparison Operators are used in
combination with integer values, the resulting value is of the type Bool (True or False). The meaning of each operator is defined as
follows:
== equal. Examples: 3==3 returns True, and 3==4
False
!= not
equal. Examples: 3!=3
returns False, and 3!=4 True
> greater
than. Examples: 3>2 returns True, and 2>3
False
< less
than. Examples: 3<3 returns False, and 2<3
True
<= less
than or equal. Examples: 3<=2
returns False, and 2<=3 True
>= greater
than or equal. Examples: 3>=3
returns True, and 2>=3
False
The numerical operators ++, --,
+, -,
*, /,
and %.
Numerical Operators result in a value
of type Int, Float or Double.
Certain of these Numerical
Operators will have different interpretations depending on where they are
placed in relation to the values they act on.
Numerical Operators have the
following definitions:
++ When
++ is placed before an operand, it
is a prefix increment, and returns a value before an increment;
when
++ is placed after an operand, it is
a postfix increment, and returns a value after an increment.
++
is a name operator, which means that it can't act on literal or constant
integers. For instance, 3++ would be an
error, because 3 is an integer constant, and for that reason the increment
operator cannot change it. However, let
number be an attribute name, and let
the current value associated with that attribute equal 3. Now, when ++ acts on number as a prefix increment, i.e., ++number, the expression returns a value of 3 but the attribute
number is increased to 4. When ++ acts
on number as a postfix increment,
i.e., number++, the attribute number is
increased to 4 and the expression returns a value of 4 (which is the result
after an increment)
-- When
this is placed before an operand, it
is a prefix decrement, and it will
return the value before a decrement; when it is placed after an operand, it is a postfix
decrement, and returns the value after a decrement. -- is also a name operator. See comments and examples on the operator
++.
+ When this is placed before an operand, it is a positive sign, returning the same value
of the integer. For instance, +3 returns
3. When it is placed between two operands, it is an addition
operator. For example, 2+3 returns
5. If one of the two operands is a
floating-point number, the result of the addition operator will be a floating-point
number. For example, 2+3.3 returns the
floating-point number 5.3.
- When this is placed before an operand, it is a negative sign, returning the negative
value of that integer. For example, -3
will return -3. When this is placed between two operands, it is a subtraction operator. For example, 5-2 returns 3. If one of the two operands is a
floating-point number, the result of the subtraction operator will also be a
floating-point number. For instance,
5-3.3 returns a floating-point number 1.7.
* Multiplication, when placed between
two operands. For example, 3*4
returns 12. If one of the two operands
is a floating-point number, the result of the multiplication operator will be a
floating-point number. For instance,
3*3.3 returns a floating-point number of 9.9.
/ Integral division with truncation, when placed between two operands. As an
example, 14/3 returns 4. If one of the
two operands is a floating-point number,
the result of the division operator will be a floating-point number. For instance, 14/3.5 returns a
floating-point number 4.0.
% Remainder, when placed between
two integer operands. For example,
14%3 returns 2. A remainder cannot act on floating-point numbers.
3.2. Floating-Point
Number
The type of floating-point number described
in 3.2.4.3 Floating Point Literal is
either Float or Double, representing the
single-precision and double-precision floating-point numbers.
ScriptV provides the following operators
that can act on floating-point values:
The comparison operators ==, !=,
>, <
, >=, and <=, which result in a value of type Bool (True or False).
The meaning of these operators is defined as follows:
== equal. Examples: 3.2==3.2 returns True, and 3.2==4.2
False
!= not
equal. Examples: 3.2
!=3.2 returns False,
and 3.2 !=4.2 True
> greater
than. Examples: 3.2 >2.2
returns True, and 2.2
>3.2 False
< less
than. Examples: 3.2 <3.2 returns False,
and 2.2 <3.2 True
<= less
than or equal. Examples: 3.2
<=2.2 returns False,
and 2.2 <=3.2 True
>= greater
than or equal. Examples: 3.2
>=3.2 returns True,
and 2.2 >=3.2 False
The numerical operators +, -,
*, and /,
which result in a value of type Float or Double.
The meaning of these operators is defined as follows:
+ When this is placed before an operand, it is a positive
sign, returning the same value of the number, for instance, +3.2
returns 3.2. When it is placed between two operands, it
is an addition operator, for instance, 2.2+3.2returns 5.4.
- When this is placed before an operand, it is a negative
sign, returning the negative value of the number, for instance, -3.2
returns -3.2. When it is placed between two operands, it
is a subtraction
operator, for instance, 3.2-2.1 returns 1.1.
* Multiplication, placed between two
operands. For instance, .1.6*1.6
returns 2.56.
/ Division, placed between two
operands. For instance, 5.12/1.6
returns 3.2.
For numerical operators, if one or both of
the operands are in double precision, the result will also be in double
precision.
3.3. Character
The type of characters described in 3.2.4.2 Characters is Char,
which represents 8-bit or16-bit coded characters.
ScriptV provides the following operators
for characters:
The comparison operators ==, !=,
>, <
, >=, and <=, which result in a value of type Bool (True or False).
The meaning of these operators is defined as follows:
== equal. Examples: ‘a’==‘a’returns True, and ‘a’==‘b’False
!= not
equal. Examples: ‘a’!=‘a’returns
False, and ‘a’!=‘b’True
> greater
than. Examples: ‘b’>‘a’returns True, and ‘a’>‘b’ False
< less
than. Examples: ‘b’<‘a’returns False, and ‘a’<‘b’
True
<= less
than or equal. Examples: ‘b’<=‘a’returns
False, and ‘b’<=‘b’True
>= greater
than or equal. Examples: ‘b’>=‘b’returns
True, and ‘a’>=‘b’False
The numerical operators ++, --,
+, and -. These numerical operators result in a value of type Char or Int, depending on
the type of the second operand. The
effect of these operators on Char is
similar to the effect of operators on integers.
Floating-point numbers and characters can
be combined in numerical operations.
This will result in floating-point numbers. However, this case is rarely used. The result character after a numerical operation is dependent on
the coding method of the character set.
In ASCII coding, for example, ‘a’+1 is ‘b’. The character numerical operation deals with
the characters as integers by using its internal coding.
3.4. String
A string is a sequence of characters,
as described in 3.2.4.4 Strings. ScriptV provides a built-in class, String,
for these string values.
ScriptV provides the following operators
and functions for string values:
== equal. This tells whether two strings contain the
same sequence of characters.
+ string addition. For instance, “Hello” + ”World” returns “Hello
World”
Methods such as calculating the number of
characters of a string and locating specific characters in a string are
dependent on the applied character set. ScriptV does not have built-in
functions to support these methods. They are provided in the utility library in
the Visviva Animation Engine.
3.5. Enumeration
Enumeration is a class construct specifying
a set of scalar values whose literal expression is an identifier that is listed
in an enumeration class constructor.
An enumeration class constructor begins
with the keyword enum, and is
followed by a list of identifiers in parentheses. For example,
enum
(MON, TUE, WED, THU, FRI, SAT, SUN);
gives an enumeration type which has a list
of the names of days in a week.
Each identifier listed in an enumeration
class constructor is associated with an integer value. Unless otherwise specified, this integer
value association starts with 0 and increases by one for each enumeration
identifier. For example, MON
is associated with 0, TUE is associated with 1, WED is associated
with 2,
and so on.
Operations supported for enumeration types
are:
The comparison operators ==, !=,
>, <
, >=, and <=, which result in a value of type Bool (True or False).
When in comparison, the integer value that is associated with each
enumeration value will be used. For
instance, TUE>MON
is true, and TUE>SUN
is false.
The numerical operators ++ (increment) and -–(decrement),
which result in values of the same enumeration. The effect of these operators is similar to the effect of
numerical operators on integers, in that an increment will indicate a successor
in an identifier enumeration list. If
there is no successor, then an increment will have no effect. A decrement indicates the preceding
identifier in the enumeration list. If
there is no preceding identifier, then a decrement will have no effect.
Unlike C++, identifiers appearing in an
enumeration list can be accessed only when the enumeration type is
expected. Therefore, the same
identifiers may be used for different enumeration types, even when the
identifiers are declared in the same scope.
Consider the following two classes:
class
Spectrum1 is enum (Red, Green, Blue, Yellow, Brown);
class
Spectrum2 is enum (Brown, Yellow, Black, White, Gray);
They both have Yellow and Brown in their
identifier lists. If we have
attributes:
color1:
Spectrum1;
color2:
Spectrum2;
and the following assignments:
color1
= Yellow;
color2
= Yellow;
The first assignment uses the enumeration
value defined in the class Spectrum1,
which has the order 3 (Red=0, Green=1,
Blue=2, Yellow=3); and the second assignment uses the enumeration value
defined in the class Spectrum2, which
has the order 1.
In enumeration classes, any identifiers may
be listed as long as they are different from each other in the same enumeration
class. They cannot be used where the
enumeration type is unknown or the enumeration type is not expected. For instance, the expression
Yellow>Brown
is an error, because we cannot decide which
enumeration class is used. If we use Spectrum1, it should return False; but if we use Spectrum2, it should return True.
However, the expression
color1>Brown
is correct, because we know that color1 has the type Spectrum1 and Brown
should be an enumeration identifier listed in Spectrum1.
The class Bool is a predefined enumeration class:
class
Bool is enum (False, True);
In addition to operators for other
enumeration classes, the class Bool
has the following logical operators:
&& logical
and. Examples: True && False returns False
|| logical
or.
Examples: True ||
False returns True
! logical
not. Examples: !True
returns False
Logical operators are often used with
comparison operators. For example:
0<x && x<100 x is greater than 0 and less than 100
3.6. Set
Set
is a class construct specifying a set of scalar values whose literal expression
is a set of identifiers that is listed in the set class constructor.
Consider the enumeration type we gave in
the previous section:
class
DayOfWeek: enum (MON, TUE, WED, THU, FRI, SAT, SUN)
The value of the enumeration type is a single
day of a week. It can be Monday, Tuesday, and so on. In some
situations, we may need a description of a set of days. For example, an airline flight from Chicago
to Tokyo is scheduled on Monday, Thursday, and Saturday of every week. To
describe this, we can use a set type:
class
DaysInWeek: set (MON, TUE, WED, THU, FRI, SAT, SUN)
The value of a set type is not a single
identifier, but rather a number of identifiers listed in the class
constructor. For example,
flight_days:
DaysInWeek = (MON, THU, SAT);
the value of the attribute flight_days contains three days: Monday, Thursday, and Saturday.
If there is only one day in a set value,
the parentheses may be omitted:
flight_days:
DaysInWeek = MON;
It is important to remember that this is a
different case than an enumeration value.
Operations supported for set types are:
The comparison operators ==, !=,
>, <
, >=, and <=, which result in a value of type Bool (True or False). The meaning of these operators
is defined as follows:
== equal. If two values of the same type contain the
same set of identifiers, it returns True,
otherwise it will return False;
!= not equal. If two values of the same type contain the
same set of identifiers, it returns False;
otherwise it will return True;
> greater than. If the left operand contains more set
identifiers in addition to those contained in the right operand, it returns True, otherwise, False. For example, (MON,THU,SAT)
> (MON,SAT) returns True;
< less than. If the left operand contains less set
identifiers than those contained in the right operand, it returns True, otherwise, False. For example, (MON,THU)<(MON,THU,SAT)
returns True;
<= less than or equal. If the left operand has equal or fewer set
identifiers than those contained in the right operand, it returns True.
Otherwise, it will return False. For example, (MON,SAT)<= (MON,SAT) returns
True;
>= greater than or
equal. If the left operand contains
an equal or greater number of set identifiers than those contained in the right
operand, it returns True, otherwise, False.
For example, (MON, THU, SAT) >= (MON, SAT) returns True.
The set operators ~, |,
+, -,
^, &,
has, and ?, which result in a
value of the same set. The meaning of
these operators is defined as follows:
~ Complement. This returns a set value that holds the set
identifiers not contained in the original value. For example, ~(MON,THU,SAT) returns ~(TUE,WED,FRI,SUN);
| or + Set
or. This returns a set value
that holds the union of the identifiers contained in both the left and the
right operands. For example, (MON,THU)|SAT
returns (MON,THU,SAT);
& Set and. This returns a set value that holds the common identifiers
contained in both the left and the right operands. For example, (MON,THU)&(SAT) returns (), an empty set value.
- Set
subtract. This returns a set value that holds the identifiers contained
in the left operand and not contained in the right operand. For example,
(MON,THU)-(THU,WED) returns (MON). ^ Set xor. This returns a
set value that holds the identifiers contained either on the left, or on the
right, but not in both of the operands. For example, (MON,THU)^(THU,WED)
returns (MON, WED).
has, ? Set inclusion test. This returns True if the set value that appears on the left contains the
identifier on the right; otherwise, it returns False. For example,
(MON,THU)?THU returns True; and
(MON,THU)?WED returns False.
Like identifiers of an enumeration class,
any identifiers may be listed in set classes
as long as the identifiers are different from each other in the same set class. However, they cannot be used where a set class is unknown, or where a set value is not expected.
Therefore, the above example expressions that explained the meaning of
the set value operators would
actually be invalid in ScriptV. Consider:
(MON,SAT)<=MON
The first operand is a set value, but we
cannot judge the type of the value based on the identifiers because there may
already be another set class that
uses the same identifiers. In fact, we
would seldom write an expression like this because we already know that (MON,SAT)<=MON
is False.
Set operators, therefore, are useful only
when they are applied to attributes or parameters. For instance, we can use
flight_days>=(MON,SAT)
to check whether flight_days contains Monday
and Saturday.
3.7. Tuple
A tuple consists of a number of values
separated by commas, such as:
(25,
42.3, “hello”)
Tuples may be nested:
(25,
42.3, “hello”, (25, 42.3, “hello”))
Tuples have many uses, such as (x,y,z) for 3D coordinates and (r,g,b,a) for RGBA color.
Each tuple has a class that is defined by a tuple class construct and contains a
list of element declarations in a pair of parentheses. For instance, the declaration
class Font is tuple
(
family_name: String;
style: FontStyle;
weight: FontWeight;
size: Int;
char_set: CharSet;
);
defines a tuple class, Font, which contains a total of five elements: the family_name of the font, the style (bold, italic, underline, etc.),
the weight (relative thickness of
the stroke), the size in points, and
the character set (ASCII, French,
German, Japanese, etc.).
An element declaration in a tuple class has
the same syntax of the attribute declarations we discussed in section 4.2.1., Attribute Declaration.
Like an
attribute declaration, a tuple element can also have default values declared in
the tuple class. For example:
class Font is tuple
(
family_name: String
= null;
style: FontStyle
= ();
weight: FontWeight
= Normal;
size: Int
= 12;
char_set: CharSet
= UseDefault;
);
The value for a tuple element in a tuple
value can be either anonymous or named.
In an anonymous tuple value, values for tuple elements must be given in the order
in which the elements were declared in the tuple class. For example, a tuple value for the Font
class may be written:
(“Times New Roman”, Italic, Heavy, XLarge, Ascii)
In this tuple
value, the first string is for the name of the font; Italic represents the style of the font; the weight of the font is
set as Heavy, the size of the font is
XLarge; and the character set is
ASCII.
In ScriptV, tuple values may be assigned to all elements,
or only to interested elements. For
this reason, only interested elements require names. For instance, the font:
(char_set=ChineseBig5, size=XLarge)
gives values for two elements only:
traditional Chinese for the char_set,
and an "extra large" font size for the size. The values for the
other three elements will be the default values named in the original tuple value: null for family_name; empty set for style; and Normal for weight.
3.8. Stream
and Array
A stream
or array is a class construct that
provides a specific container in which the value of each declared element is
accessed by an integer index beginning with zero. While the number of elements that
can be declared in a stream is
unlimited, an array is a specific
stream with a fixed number of elements.
ScriptV uses “[]” for stream and
array class constructors. Examples:
Float[] //
a float stream that may contain any number of float values
Float[4] //
a float array that contains four float values
To access an element of a stream or array,
we use an integer index. Consider the
attribute fs in a float stream:
fs:Float[];
We use the expression fs[0]
to access the first element in the stream, fs[1] to access the second
element in the stream, and so on. Thus,
fs[0]= fs[1];
means “assign the value of the second
element in the stream to the first element in the stream.”
To assign a value to a stream or an array
as a whole, we can use a tuple value.
The only requirement is that the type of the tuple element must match
the element type of the stream, or the array.
There is an additional requirement for arrays, in that the number of
elements contained in the tuple must be equal to the element number that was
specified in the array. Examples:
fs:Float[];
fa:Float[4];
fs = (23, 45, 56, 43, 21, 435.4, 33.3, 0);
fa = (-23, 43, -0.34, 45);
A stream or array has the function Length.
This function may be used to get the number of elements contained in the
stream or array:
ele_num: Int = fs.Length();
A stream also has another function, called Append. This function can be used to add new
elements to the rear of a stream:
fs . Append ( 0.10 ) ;
3.9. Super
A super
is a class construct that provides a type that contains a list of possible
types. For example,
class Value is super ( Int, Float, Char );
defines a super class called Value.
Value may be one of the three
types: Int, Float, or Char. Therefore, if we have the variable:
n: Value;
The variable may be assigned to an integer,
to a float, or to a character:
n = 10;
n = ‘c’;
A super
class is an abstract class and cannot be used to create objects. For example:
class PossibleType is super ( Button, MenuItem );
We cannot use PossibleType to create an object.
Instead, objects of PossibleType
should be either Button objects or MenuItem objects.
Functions that apply to a variable of a super type must be applicable to each of
the types listed in the parentheses.
4. Expressions
Much of the work in programming is done by
writing expressions. Expressions are meaningful combinations of
symbols that accurately convey the writer's intentions in a program.
ScriptV has a set of operators rich enough to enable
access to most of the operations provided by the underlying platforms that most
applications require. ScriptV also
provides a very flexible mechanism with which users may define their own
operators and expression syntax. For
any Java or C++ expression, there is a superior expression in ScriptV. In addition, ScriptV expressions are much
richer than Java and C++ expressions due to ScriptV's ability to support
user-defined expression syntax.
Though ScriptV adds much more flexibility
than other languages, the design of ScriptV expression is orthogonal. ScriptV derives its diversity and
flexibility from its design philosophy of unification. ScriptV treats all operators by a set of
common rules, instead of using additional operators and operator formats.
4.1. An Introduction to ScriptV Expressions
Expressions combine the names of objects,
classes, attributes, parameters, and literals with operators and functions to
produce new values. Expressions provided by a typical programming language are:
• Literals (integers, character strings, etc.)
• Names (object names, class names, function
names, variables, references, parameters, etc.)
• Function calls
• Expressions with Operators
Simply put, an expression is a formula that stands for
something. In previous chapters, we
already introduced the concept of literals in the form of integers and
characters. Literals also included the
names of objects, classes, functions, and parameters, as well as the concept of
function calls. Although we didn’t
mention it before, all of these are expressions in ScriptV. Consider the following list:
‘c’ an expression for a
character literal
24.6 an expression for a
floating-point literal
Bool an expression for the
enumeration type Bool
True an expression for an
enumeration value of the type Bool
button
an expression for
an object named button
button.state
an expression for an
attribute of the object button
button.Move
(2, 3) an expression to call
the Move function of the object button
The expressions introduced above are only primary
expressions. An expression can be a
combination of the primary expressions above with operators and parentheses, as
we observe in the following mathematical formula:
a ´ ( b +
45.0 )
Here, a and b are variable names and 45.0 is an
floating-point number. 45.0, a, and b are primary expressions that are combined with the operators ´ and
+,
as well as parentheses, to constitute a compound expression.
4.2. Operators
In mathematical formulas, an operator stands for a
function in calculation, and the same holds true for operators in ScriptV. An operator is actually a function. In
Chapter 2.2.4., Function Declaration,
we discussed the expression of an ordinary function call. Operator functions, however, are called in a
specific and distinct fashion.
In Chapter 3, Basic
Types, we introduced a number of operators for basic types, such as +, *,
-, and /. Sharp readers may have
already noted a particular arrangement
in expressions for operators. For
example:
x + 40
In this expression, the operator + appears in the middle
of the expression (between the two operands). Compare this expression with the
ordinary function call format
x.Add(40)
There are two differences. Firstly, the character “.” is not used between the object name x and the function name +, as opposed to
an ordinary function call, where the dot must be used. Secondly, in the operator expression, the
parentheses are not required for the actual input parameter 40.
Certain other operators may have an even more distinct
arrangement, as in:
- x
The operator – appears before the object x.
This operator is called the prefix operator.
We know that in mathematical formulae, there is a rule
known as precedence. For
example, multiplication has a higher precedence than addition, according to
this rule. Therefore, in the formula:
x + 40
´ y
“40 ´ y” is performed first, and then the addition is
performed.
In ScriptV, operators also have precedence. When an operand can be grouped with either
of two operators, the operand will be grouped with the operator that has the
highest precedence. In other words, an
operator that has a higher precedence is always performed in an expression
before an operator with a lower precedence.
However, this precedence can be changed by parentheses, as shown in the
example below:
(x +
40) ´ y
In this expression, “x+40” is performed first,
before the multiplication.
Another rule about operators is known as associativity. When two operators in an expression have the
same precedence, will the operands in this expression be grouped from left to right
or from right to left? For example, in
the expression:
x + 40
– y
Should the addition be performed first, and then the
subtraction (by grouping the operands from left to right), or should the
subtraction be performed first and then the addition (by grouping the operands
from right to left)? The answer is
determined by the operator’s associativity.
If operators in the same precedence have left associativity, then the operators are performed from left to
right. If operators in the same
precedence have right associativity,
the operators are performed from right to left.
In mathematical formulae, the operators + and – have left associativity.
Operators in ScriptV are divided into 14
groups. Below is a list of the ScriptV
built-in operators, in order, from the highest precedence to the lowest
precedence. Operators with the same
precedence are grouped together and have the same associativity. Items listed in the first group are used for
primitive expressions. These items are
not operators, but have the highest precedence in the composition of an
expression.
Operators in
precedence 13 (used for primitive expressions)
literal
name
string
concatenation
( ) tuple object composition brackets
:: global scope selectors
:: scope selectors
Operators in
precedence 12 (used for terms, or simple expressions)
[ ] array subscribe left
. member
selector (by object) left
object
instantiation (empty name) left
Operators in
precedence 11 (used for prefix expressions)
++ prefix increment right
-- prefix decrement right
+ arithmetic
positive right
- arithmetic
negative right
~ set
complement right
! logical
NOT right
Operators in
precedence 10 (used for incremental expressions)
++ postfix increment left
-- postfix decrement left
Operators in
precedence 9 (used for multiplicative expressions)
* multiplication left
/ division left
% remainder left
& set AND left
Operators in
precedence 8 (used for additive expressions)
+ arithmetic
addition left
- arithmetic
subtraction left
| set
OR left
Operators in
precedence 7 (used for shift expressions)
<< left shift left
>> right shift left
<+ left rotate shift left
+> right rotate shift left
Operators in
precedence 6 (used for relational expressions)
< less than left
> greater than left
<= less than or equal left
>= greater than or equal left
== equal left
!= not equal left
Operators in
precedence 5 (used for logical AND expressions)
and, && logical AND left
Operators in
precedence 4 (used for logical OR expressions)
or, || logical OR left
Operators in
precedence 3 (used for conditional expressions)
has, ? conditional left
Operators in
precedence 2 (reserved for user-defined operators)
left
Operators in
precedence 1 (used for assignment expressions)
= assignment right
:= assignment right
*= multiplication assignment right
/= division assignment right
%= modulus assignment right
+= addition assignment right
-= subtraction assignment right
&= set AND assignment right
|= set OR assignment right
^= set XOR assignment right
Operators in
precedence 0 (used for expression lists)
, sequential
evaluation left
There are 14 precedence groups. Each group is assigned with a number, as
shown in the above table. All
user-defined postfix operators have a precedence of 2. All of the user-defined prefix operators
have the precedence 11. However, if a
user-defined operator uses the same name as one of the built-in operators, the
precedence of the user-defined operator will use the precedence of the built-in
operator.
4.3. Primitive Expressions
There are four kinds of primitive
expressions: names, literal constants, tuple
values, block closures and class closures.
Literals were discussed in section 3.3.4. Literal, and tuple values were
discussed in section 5.7. Tuple. In this section, we will discuss names, block closures, and class closures.
4.3.1 Name
A name can be an identifier of a
class, object, function, attribute, parameter, or variable. A name may also be an operator name of a function.
Names declared in an inner scope may often
mask a name declared outside. Consider:
class
TextExtrude: World3D
{
text: String;
object group: Group3D
{
object text: Text3D
{
}
enter ()
{
text //
refer to inner object of Text3D
}
}
}
The Text3D
object that was declared in the group object
hides the string text that was
declared outside of the group
object.
ScriptV has the following keywords reserved
for specific names:
self Used in an expression to reference
the innermost object or an instance of the innermost class that contains a self expression.
owner Used in an expression to reference the
immediate enclosing (parent) object of a self.
root Used in an expression to reference the
outermost object that contains a self. Root will
usually refer to the application environment.
Consider the following example:
class
TextExtrude: World3D
{
object group: Group3D
{
self //
self is the object group
owner // owner is
the instance of TextExtrude
object material: Material
{
self //
self is the object front
owner // owner is
the object group
}
}
enter () // in initializer body
{
self //
self is the instance of TextExtrude
owner // owner is
the enclosing object of self
}
}
In cases where an outside text must be
accessed from inside, a scope resolution operator “::”
can be used:
class
TextExtrude: World3D
{
text: String;
object group: Group3D
{
object text: Text3D
{
}
enter ()
{
TextExtrude::text // refer to the outside object of string
}
}
}
A scope resolution operator (::) compels
ScriptV to automatically search for the next outermost object that has the same
name that was typed in front of the scope resolution operator. A scope resolution operator can also be used
for self and owner.
class
TextExtrude: World3D
{
object group: Group3D
{
object material: Material
{
TextExtrude::self // self
is the instance of TextExtrude
TextExtrude::owner // owner
is the enclosing object of TextExtrude
::self
}
}
}
Scope resolution operators can only be used
with a class name. To refer to object
names, we can always use an object’s name directly.
In addition, a global scope resolution operator can be used. Its format is the same as a scope resolution
operator, but does not require a class name in front of it. A global
scope resolution operator will automatically resolve to the outermost
scope.
4.3.2 Block Closures
A block
closure encapsulates a sequence of declarations and statements that can be
used as values for block attributes and block parameters.
Consider the type StateTransition, which has a block attribute, action:
class
StateTransition is tuple
{
source_state: String;
destination_state: String;
action: Block;
}
A block closure can be assigned to the action attribute. When a state transition matches this
specification, the statements contained in the action can then be performed.
The expression for a block closure is
sequence of declarations and statements contained in a pair of curly brackets,
or a pair of begin and end brackets.
For instance:
begin
i: Int;
for (i=0; i<20; i++) do channels[i] =0;
slot.Open();
end
4.3.3 Class Closures
A class
closure can be used to define classes in a dynamic manner. Class
closures are primitive expressions most often used as values for class
attributes and class parameters.
Consider the function Create defined in the class Object:
class
Object
{
func Create (name: String, cls: Type of
Object);
}
The Create function is used to create a child object in an enclosing
object. The first parameter in this
function is the name of the child object, and the second input parameter is the
class of the child object created. The
child object created here must be from the class of Object.
Suppose icon_list
is our enclosing object, and Icon is
a class. In this case, the expression:
icon_list.Create(“icon1”,
Icon);
creates an object named icon1 in
the icon_list object. The icon1
object created by this expression is from the class of Icon.
If we were going to add specializations to
the Icon objects created with this
function, such as identifying names that appear below each Icon object, it would not be adequate to use the class Icon in our Create function, because we would need to type out the same object.Create statement for each
item. Additionally, since we will want
a new icon for every object that is added to the icon_list object, this part of the program needs to be dynamically
defined.
In cases like these, when we want to add
specializations based on dynamic conditions, we can use a class closure. Below, we’ve added a class closure to a foreach statement:
foreach
(item in some_dictionary)
icon_list.Create
( item.Name(),
class Icon
{
object_in_icon = item;
}
);
A foreach
statement performs an iterative statement for each identifier in an expression. Here, the expression is the primitive expression some_dictionary. The
identifier is item and the iterative
statement, is, of course, Create. In plain English, the foreach statement can be translated as "for each 'item' in the 'some_dictionary' object, create an icon_list object."
The Create function input parameter that assigns a name to each created
object is written in our expression as
( item.Name(),
Name
is an output function that returns a string value to the object item.
Therefore, the icon has the same name as the name of the object
represented by the icon. The second
half of the Create expression
describes the icon class from which the icon should be created:
class Icon
{
object_in_icon = item;
}
It is a class closure and it performs two
responsibilities in our foreach
statement.
(1) It states
that the objects created in the Create
expression are going to be from this Icon class.
(2) It assigns
the dynamic object item to the
attribute object_in_icon.
In this manner, the
objects in icon_list are created from
a dynamically defined class.
As seen in the above example, a class
closure is actually an anonymous class declaration. Unlike a normal class declaration, a class closure can be used as
an expression and can be enclosed in a block (see Section 7.2 Compound Statements).
Variables declared in a block outside of a
class closure can be used for attribute initialization. As shown in the above example, the attribute
object_in_icon is assigned to the
dynamic value held by the block variable item. However, a block variable cannot be used in
a function that is contained in a class
closure. For example,
foreach
(item in some_dictionary)
icon_list.Create
( item.Name(),
class Icon
{
object_in_icon = item;
func OnCursorOver() {
item.HighLight(); };
}
);
The function OnCursorOver uses the block variable item, which is not correct.
A block variable is valid only within the block. The program control will eventually go
outside of the block, and when it does, the block variable will have no
effect. However, an object created from
a class closure will outlive a block variable. The function OnCursorOver, therefore, can be called
through an object when there is no such block variable. The correct usage would be:
foreach (item in some_dictionary)
icon_list.Create
( item.Name(),
class Icon
{
object_in_icon = item;
func OnCursorOn()
{object_in_icon.HighLight(); };
}
);
The block variable’s value is kept in a
variable inside of a class closure in order that the value may be used after
the block disappears.
4.4. Simple Expressions
Simple expressions are composed of the
expressions discussed in section 6.3
Primitive Expressions and operators with the highest precedence, i.e., the
precedence 13 listed in section 6.2. Operators.
A simple expression can be one of the
following:
Primary (Primitive) Expression
Subscript Expression
Object Instantiation
Member Expression
Function Call
4.4.1 Subscripting Expression
A subscripting expression occurs in
the following format:
SimpleExpression [ ExpressionList ]
A subscripting expression is used for operators
whose name head is the character '['.
The expression list in the brackets must match the operator's interface
specification.
Though the operator [] can be used for
other purposes, it is usually used as a built-in operator for stream or array
subscripting. In stream or array
subscripting, a simple expression, usually a stream name or an array name,
assigns values to a stream or array object, and the expression list contained
in the brackets is an integer expression.
For instance,
f[i+3]
is a subscripting expression where f may be an attribute name whose type is
a stream, and i+3 is an expression
that evaluates an index in integer.
4.4.2 Object Instantiation Expression
An object instantiation expression is
used to create a new object:
SimpleExpression Expressionoptional
where the simple expression (commonly a
class name) assigns the values of a class. The expression that follows the
simple expression is optional, and provides an input that matches the class
initializer interface. The object instantiation
expression returns an object of the given class. Consider the class:
class
TextExtrude: World3D
{
enter
(text:String; extrusion:Float=0.5; spacing:Float=-0.1)
{
}
}
and the object instantiation expression:
TextExtrude
(“Hello, World!”, 0.8)
The simple expression for the class is the
class name TextExtrude, and the
expression for initializer input is a tuple: (“Hello, World!”, 0.8). The expression evaluates a new object of
the class TextExtrude.
4.4.3 Member Expression
A member expression is used for
operators whose name head is the character '.':
SimpleExpression . Name
where the simple expression evaluates an object, and the
name is the attribute name, or the child object name, or the function name of the object. It gives a value to the attribute, the child
object, or the function given by the name.
Consider:
class
TextExtrude: World3D
{
object group: Group3D
{
scaleFactor: Float[];
}
enter ()
{
group.scaleFactor = (1.5,2.8,2.8);
}
}
The expression group.scaleFactor assigns values to the scaleFactor attribute of the group
object.
4.4.4 Function Call Expression
A function call expression is used to
call the function of an object and is written in the following format:
SimpleExpression Expressionoptional
where the simple expression (commonly a
member expression with an object name and a function name) gives a value to a
function. The expression that follows is optional, and may provide an input
that matches the function input interface.
The function call expression returns an object of the output type
specified in the function’s interface.
If the function does not have an output, the expression evaluates to void.
Consider:
class
TextExtrude: World3D
{ object group: Group3D
{ func SetShininess(shininess: Float) {...};
}
enter ()
{ group.SetShininess(0.9);
}
}
In the function call expression:
group.SetShininess(0.9);
The function is SetShininess. The object
that performs the function is group. The input is (0.9). The expression
evaluates to void since the function SetShininess does not have an output.
If the object that performs the function is
a self, it is not necessary to put self in the expression. Consider:
class TextExtrude:
World3D
{ object group: Group3D
{ func SetShininess(shininess: Float) {/*...*/};
enter ()
{ SetShininess(0.9);
}
}
}
The performer of the function SetShininess is group, which is self in
the scope of the group object, so we
do not need to explicitly provide it.
4.5. Prefix Expressions
To call a function or operator of an
object, we usually give the object first, then the function or operator. For instance,
x + 4
First, we gave an integer object (which is
an integer variable). Then we gave the
function, namely, +, and finally the input, which is the integer literal 4.
To call a prefix operator, use a different
syntax: give the operator name first, then the object that performs the
operator. For instance, the expression
++x
gives the operator first, then the object.
The above expression is called a prefix
expression, which is composed of the simple expressions discussed in
the previous section and the prefix operators in the Precedence 11 group listed
in section 6.2., Operators.
Here is an example of user-defined prefix
operators and prefix expressions that use these operators:
class
Image
{
function operator ==> prefix () <==;
// compress;
function operator <== prefix () ==>;
// decompress;
};
object
my_image: Image;
==>
my_image <==; // compress
<==
my_image==>; //
decompress
<==
==> my_image <== ==>; // compress then decompress
4.6. Compound Expressions
Compound expressions are made of operators
and expressions from the highest precedence.
As shown on the Precedence and
Associativity of Operators Chart in 6.2, the primitive expressions are in Precedence
13. Primitive expressions with
operators of the highest precedence make up simple expressions. Simple expressions with operators of the
second highest precedence (precedence 12) constitute prefix expressions. Prefix expressions with operators of the
third highest precedence (precedence 11) constitute incremental expressions,
and so on.
By same syntax rule, compound expressions
above prefix expressions are made of operators and expressions at the next
highest precedence. Therefore, we can
discuss compound expressions at Precedence n
in terms of expressions at Precedence n+1.
Let’s consider the following expression:
x*3+y*4
The operator * has a higher precedence than
the operator +
(* = Precedence 9, and + = Precedence 8). Therefore, x*3 and y*4 combine into
two expressions at a higher precedence (namely, Precedence 9), and then the two
expressions at Precedence 9 combine into an addition expression at Precedence
8. In other words, to evaluate the
expression, we do x*3 and y*4 first, and then use the
results to perform the final addition.
If operators have the same precedence, then
we will evaluate the expression from left to right if these operators have the
left associativity. For example, in:
x-3+y-8
we will do x-3 first, then add y
to the result, and finally, subtract 8 from the result.
Of course, if operators with the same
precedence have right associativity, then the expression evaluates from right
to left.
Parentheses can be used to change
precedence. Putting an expression in a
pair of parentheses will make the expression a tuple value, i.e. a primitive
expression, and consequently place the expression into Precedence 13. For instance, consider
x*(3+y)*4
where (3+y) is a primitive
expression. Here, we will perform (3+y)
first, then we’ll multiply that result by x, and finally multiply that
result
by 4.
4.7. Assignment Expression
An assignment expression is used to change
the value of an object attribute, parameter, or variable:
object.x
:= left_margin + 0.2;
or
object.x
= left_margin + 0.2;
The above two assignments have the exact
same meaning. ScriptV encourages the
use of the operator “:=” for assignment expressions, but programmers who
are used to C++ or Java’s assignment operator may also use “=”.
The left side of the assignment operator
must evaluate an object attribute, parameter, or variable. The following assignment is not acceptable:
3
:= object.x + 0.2;
3 is an integer literal, and
there is no way to change its value. It
is also not acceptable for the attribute, parameter, or variable at the left
side of the assignment to be a read only constant.
In addition to the operators =
and :=,
there are several other assignment operators defined in Precedence 1. They can
be used for attributes, parameters, or variables in certain kinds of
classes. The multiplication assignment,
for instance, can only be used for arguments in classes that have a
multiplication operator, i.e. the class int,
float, and double. Consider:
x:
Float =2.0;
s:
String = “hello,”;
x
*= 34.5;
s
*= “world!”;
The last assignment is invalid because
string does not have multiplication.
The meaning of x *= 34.5 is to assign x the value of the
result of the multiplication of 34.5 to x’s original
value, 2.0. The variable x holds 79.0 after the
assignment. Here is a list of classes
on which it is possible to use assignment operators:
= assignment Any
:= assignment Any
*= multiplication assignment Int,Float, and Double
/= division assignment Int,Float, and Double
%= modulus assignment Int
+= addition assignment Char, set, enum, Int, Float, and Double
-= subtraction assignment Char, set, enum, Int, Float, and Double
&= set AND assignment set
|= set OR assignment set
^= set XOR assignment set
4.8. User-Defined Operators
A user-defined operator is basically the same as a
function, except for the function name, which uses the keyword operator. Here are some examples of operator functions:
func
operator <<= (Int)
func
operator [ (index: Int) ] : ElementType
func operator
set (Char[]) to (Char[])
func
operator select (name: Char[]) from (Database): Object
func
operator perform (a:Action) on (s:Site) catch (ev:Event...)
Certain operator interfaces are specified with a number
of tuple types that intersect the succeeding parts of a complete operator
name. For instance, the operator name perform-on-catch is separated into three
words. In these kinds of operator interfaces, only the first word of the name (perform) is
significant. The other words (on,
catch)
serve as suffixes. These suffixes are
used to separate actual parameters presented in an input list when these
operators are called. Therefore, we can have the expression:
object_x
perform some_action on that_site catch data_input_event
Let us consider another example:
class
Robot
{
func operator
bring
(obj:Any)
to (who:
Person)
from (where: Room);
};
The operator bring to from has three character strings, and bring is the operator name head.
Let robot be an object of Robot:
object
robot: Robot;
An expression to call the bring function of a robot will be
written in the same format that was defined in the operator interface:
robot
bring projector to me from southwest_conference_room;
If a user-defined operator uses the same
name as a built-in operator used in simple expressions, the operator must be
defined in the same format as the built-in operator is defined. In this case, the type of the input
parameters can be changed.
There is no rule to restrict a user from
using other built-in operator names. However,
the precedence and associativity of the operator will be the same as the
precedence and associativity of the built-in operator name.
An example:
class
Pipe
{
function
operator >> (Pipe): Pipe;
enter
(integer);
};
pipes:
Pipe[4] = ( Pipe(0), Pipe(1), Pipe(2), Pipe(3) );
pipes[0]
>> pipes[1] >> pipes[2] >> pipes[3];
There are certain restrictions that come
into play when operator characters are combined into an operator string. These restrictions are discussed in section 3.3.3, Operator String.
5. Statements
ScriptV provides the usual assortment of statement forms
found in most algebraic programming languages.
These statement forms include conditional statements, loops, type check
statements, and scope statements.
ExpressionStatement
CompoundStatement
IfStatement
WhileStatement
DoStatement
ForStatement
ForEachStatement
SwitchStatement
AssumeStatement
InspectStatement
WithStatement
BreakStatement
ContinueStatement
ReturnStatement
NullStatement
5.1. Expression Statement
Any expression can be treated as a statement by writing
the expression followed by a semicolon:
Expression ;
The statement is executed by evaluating the
expression and then discarding the value, if any. An expression statement is useful
only if the evaluation of the expression involves a side effect, such as
assigning a value to a variable or performing input or output. In most cases, an expression statement is an
assignment, an increment or decrement operation, or a function call.
Here are some examples of expression
statements:
x:=y;
++x;
print("name",
age, title);
5.2. Compound Statement
A compound statement consists of a
(possibly empty) sequence of declarations followed by a (possibly empty)
sequence of statements, all enclosed in braces:
Labeloptional{ BlockDeclarationListoptional StatementListoptional
}
The braces { and } are used to group declarations and
statements together into a compound statement, also known as a block, so that they are syntactically
equivalent to a single statement. Compound statements formatted in this manner
can appear anywhere a single statement can.
A compound statement may have a label:
Identifier:
An identifier of a label is used for break and continue
statements, which are discussed in Section
7.5.
A compound statement is normally executed
by first sequentially processing all the declarations one at a time, and then
sequentially executing all the statements one at a time. Execution ceases when the last statement has
been executed or when control is transferred out of the compound statement
through the execution of a return, continue, or break statement.
Following is an example of a compound statement:
{
x, y:
Float =2.0;
y :=
x+4.5;
print("name",
age, title);
}
A block declaration list is a number of variable
declarations. Each variable declaration
consists of a list of identifiers for variable names and a class name, such as
x, y: Float;
The class Float
means that the variables listed in the declaration can hold values of
floating-point numbers. Variables may have their initial values at the time
they are declared:
x, y: Float = 0.0;
Both x and y receive an initial value,
which is 0.0.
The value associated with the variable can be changed later by an assignment
statement:
x:=2.0;
Now, the variable x will have the value 2.0.
5.3. Conditional Statement
A conditional
statement is used to express decisions, such as whether a statement should
be executed based on some condition. A conditional statement begins with if and may include an optional else clause, as in the following format:
if ( Expression ) Statement1
if ( Expression ) Statement1 else
Statement2
In a conditional statement, the expression
within the parentheses after the keyword if
is evaluated first. If this controlling
expression’s value is a nonzero value, then the statement1 is executed. If
the value of the control expression is zero and there is an else clause, the statement2 in the else clause
is executed. If an else clause is not presented, the execution proceeds immediately to
the next statement after the conditional statement.
Whether or not a value returned by a
control expression is a zero value will depend on the class of the value. In
the case of an integer or floating-point number, the value will be obvious. Here
is a list of different classes and their zero values:
Char a
character with code 0 in the character set
Int integer
0
Float, Double floating-point
number 0.0
enum a
enumeration identifier (usually the first) with code 0
Bool false
set the
empty set
object reference invalid reference, to void object
stream, array invalid
stream or array, not allocated
tuple invalid
tuple, not allocated
String invalid
string, not allocated (Note: it is not an empty string)
Because the else clause is optional, there is an ambiguity when an else is omitted from a nested if sequence. This ambiguity is resolved
by assuming that an else clause
always belongs to the innermost if
statement possible. For example, in
if
(i>0)
if (x>y) z =x;
else z =y;
the else
goes with the inner if, as we have
shown by indentation. If this
construction is not desirable, then a compound statement must be used:
if
(i>0)
{ if (x>y) z =x;
}
else z
=y;
5.4. Loops: While, Do, For, and Foreach Statements
In many cases, we need to repeat a
statement that is based on parameters, steps, or counts. ScriptV provides four different statements
for this purpose: while, do, for,
and foreach. All loop statements may have a label, which
will be discussed later.
5.4.1. While Statement
A while statement consists of the keyword while, followed by a control expression
in parentheses, followed by a statement:
Labeloptional while ( Expression ) Statement
The while
statement is executed by first evaluating its control expression. If the result is not zero, then the
statement is executed. The entire
process then repeats, alternately evaluating the expression and then executing
the statement again if the value is not zero.
The value of an expression can change from time to time because of side
effects in the statement. The execution
of a while statement is complete when
the control expression evaluates to zero.
Here is an example:
i: Int
=0;
x:
Float[10];
while
(i<10)
{ x[i] = 0.0;
i++;
}
In the above expression, the while statement repeats the compound
statement:
{ x[i] = 0.0;
i++;
}
ten times to initialize the ten-element
array x. After the 10th time, the variable i is evaluated to 0 (with
the increment ++ value). When the i value reaches 10, the condition i<10 no
longer holds, and so the while
statement's execution is complete.
5.4.2. Do Statement
A do
statement consists of the keyword do,
followed by a statement, followed by the keyword while, followed by a control expression in parentheses, followed by
a semicolon.
labeloptional do Statement while ( Expression );
A do
statement executes its embedded statement first. Next, the control expression is evaluated; if the result is not
zero, then the entire process is repeated.
The value of the expression can change from time to time because of side
effects in the statement. The execution of the do statement is complete when the control expression evaluates to
zero.
We can rewrite the above while example as a do statement:
i: Int
=0;
x:
Float[10];
do
{ x[i] = 0.0;
i++;
} while
(i<10);
The while statement tests its termination
condition at the top. By contrast, the do statement tests at the bottom. Therefore, the loop body will always be
executed at least once in a do
statement, which might be necessary, or at the very least, convenient,
depending on the requirements of the program.
5.4.3. For Statement
A for
statement consists of the keyword for,
and is followed by three expressions.
These three expressions are enclosed in parentheses, separated by
semicolons, and followed by a statement:
optional for ( Expression1; Expression2; Expression3) Statement
Each of the three expressions within the parentheses is
optional and may be independently omitted, but the two semicolons separating
the expressions and the parentheses surrounding the expressions are mandatory.
It is executed as follows:
(1) If
present, the first expression is evaluated and the value is discarded.
(2) If
present, the second expression is evaluated like a control expression. If the result is zero, then the execution of
the for statement is complete.
Otherwise, proceed to step (3).
(3) The body
of the for statement is executed.
(4) If
present, the third expression is evaluated and the value is discarded.
(5) Return to
step 2.
The execution of the for statement is terminated when the second (control) expression
evaluates to zero.
Again, we can rewrite the above example in
a for statement:
i: Int
=0;
x:
Float[10];
for
(i=0; i<10; i++) x[i] = 0.0;
The for
statement is roughly equivalent to:
Expression1;
while
(Expression2 )
{
Statement;
Expression3;
}
Whether to use while or for is largely a
matter of personal preference. The for is preferable in situations where
there is a simple initialization and increment, since it keeps the loop control
statements close together and visible at the top of the loop.
5.4.4 Foreach Statement
A foreach
statement provides iteration based on
tuples, streams, arrays and composite objects.
It begins with the keyword foreach. It is followed by an identifier, a keyword in, and an expression, each within
parentheses. A statement then follows the parentheses.
This is depicted in ScriptV as follows:
labeloptional foreach ( Identifier in Expression
) Statement
The type of the expression must be
evaluated to a tuple, a stream, an array, or a reference to a composite object.
The foreach
statement is executed by repeating from first to last the execution of the
statement that was given by the expression for each element, composite object,
or child object of the composite object in the tuple, stream, or array. The identifier is the name that refers to
the current element or child object. If
there is no element or child object, then the foreach statement does nothing.
The execution of the foreach
statement is terminated when the statement has been executed for the last
element.
Once again, we can re-write the above
example as a foreach statement:
x:
Float[10];
foreach
(ele in x) ele = 0.0;
The foreach
statement is preferable when we are performing iteration on the element of a
tuple, stream, array or composite object.
Consider another example:
foreach
(bird in bird_flock) bird.FlyTo(horizon);
The foreach
statement provides a very intuitive and simple structure.
5.5 Break and Continue
It is sometimes desirable to be able to
exit from a compound statement, or from a loop, by using another method than
testing the termination conditions at the top or bottom of the loop.
The break statement:
break identifieroptional ;
and the continue statement:
continue identifieroptional ;
are both used to alter the flow of control
inside loops.
The break
is also used to alter the flow of control in switch and inspect
statements (which are described in Section
7.6 and 7.7.2.), as well as in
labeled compound statements.
5.5.1 Break
A break
statement with no label identifier attempts to terminate the execution of the
smallest enclosing while, do, for,
foreach, switch, or inspect
statement. A break statement with a label identifier attempts to terminate the
execution of the statement that has that label. When a break is
successfully executed, program control is immediately transferred to the point
just beyond the terminated statement.
It is an error for a break statement without a label identifier to appear where there is
no enclosing iterative statement or switch
or inspect statement. It is also an
error for a break statement with a
label identifier to appear where there is no statement that has that label.
Consider an example:
L1:
foreach (item in item_list)
{
foreach (sub_item in item)
{
if (sub_item.Satisfy(condition))
break L1;
}
}
The break
statement with label L1 will
terminate the outside loop that has the same statement label. If the break
statement does not have the label, the statement terminated will be the inner
loop, and control will be brought to the beginning of the outside loop.
5.5.2 Continue
A continue
statement with no label identifier attempts to terminate the execution of the
body of the smallest enclosing while,
do, for, or foreach
statement. A continue statement with a label identifier attempts to terminate
the execution of the body of the statement that has that label. Program control is immediately transferred
to the end of the body, and the execution of the affected iterative statement
continues from that point with a reevaluation of the loop test (and the
increment expression, in case of the for
or foreach statement).
It is an error for a continue statement to appear where there is no enclosing iterative
statement. In addition, it is an error
for a continue statement with a label
identifier to appear where there is no statement that has a label.
Consider an example:
L1:
foreach (item in item_list)
{
foreach (sub_item in item)
{
if (!sub_item.Satisfy(condition))
continue L1;
}
}
The continue
statement with the label L1 will
transfer the control to the end of the outside loop body, causing the item variable to get the next item in
the item list and repeat the outside loop body from the beginning. If the continue
statement does not have a label, it will transfer the control to the end of the
inner loop body, cause the sub_item
variable to get the next sub-item in item
and repeat the inner loop body from the beginning.
5.6 Switch
A switch statement
is a multi-path branch based on the value of a control expression. It consists of the keyword switch, followed by a control expression
enclosed in parentheses, followed by a switch body:
labeloptional
switch ( Expression )
{
case
CaseExpression : StatementList optional
case
CaseExpression : StatementList optional
default
: StatementList optional
}
A switch
statement contains a number of case
expressions. Each case expression
is followed by a list of statements.
The switch statement tests
whether the value of the control expression matches one of a number of constant
values provided by the case expression(s), and branches accordingly. The case labeled default is optional. If
presented, it is only executed if none of the other cases are satisfied. If there are no case matches and a default case
is not presented, then no action at all will take place.
The type of the control expression in a switch statement must be convertible to
integers.
A case expression can be in one of the following formats:
ConstantExpression
ConstantExpression1 ..
ConstantExpression2
ConstantExpression1, ConstantExpression2,
ConstantExpression3, ...
The first format presents a single constant expression.
The value of the control expression matches this case when it is equal to the
value of the case expression.
The second format presents a range between two constant
expressions. The value of the control
expression matches this case when it falls in between the two case expressions
(including the values of the two case expressions). A Range Token {..} appears between the two constant expressions
in the second format. A Range Token is
used to define a range of all the values between two scalable values of types
such as integers, enumeration, and floating point.
The third format presents a list of constant expressions.
The value of the control expression matches this case when it is equal to one
of the case expressions.
Consider the following example:
today:
DayOfWeek;
switch
(today)
{
case MON.. WED: Eat(Vegetable);
case FRI, SAT: Eat(Fish);
case SUN: Goto(Church);
default:
Eat(Bread);
}
If the today
value is Tuesday, it falls between MON
and WED, and the action Eat(Vegetable) is executed. If today
is instead Thursday, then none the cases are satisfied, and the default action Eat(Bread) is executed.
No two case
clauses belonging to the same switch
statement may have expressions that produce an overlap in the range of their
values. For instance, the switch:
switch
(today)
{
case MON..FRI: Eat(Vegetable);
case THU, SAT: Eat(Fish);
}
is incorrect, because the second
case contains THU, which forms an overlap with the first case.
A note to C++ and Java programmers: after
control is transferred to a case or default clause, the execution does not continue through successive
case statements as it does in C++ and Java.
When today is Tuesday, only
the action Eat(Vegetable) is
executed, and so the whole switch
statement is finished. Since ScriptV
provides a flexible case expression, the side effects of the continuations
observed in C++ and Java are not necessary.
In fact, if C++ and Java users forget to insert a break statement at the
end of the case, this side effect may cause unexpected errors.
5.7 Type Check Statements
It is sometimes necessary to check the
exact type of an object to perform a particular action. Given a variable of a
type Frame, for instance,
x:
Frame;
x could refer to several different
kinds of frames. Suppose that we want
to know whether or not x is an
animation frame so that we may instruct ScriptV to perform a particular action
that is specifically related to animation frames.
The assume
and inspect statements provide us
with the ability to make use of the actual type of the object.
5.7.1 Assume Statement
The assume
statement is another conditional statement.
It tests the type of a control expression and then decides whether the
statement that follows the parentheses should be executed.
The assume
statement consists of the keyword assume, followed by a control expression
enclosed in parentheses, and finally a statement:
assume(
Tagoptional
Expression is AssumeType ) Statement
assume(
Tagoptional
Expression is AssumeType ) Statement else Statement
The tag, like a statement label, is an
identifier with a colon:
identifier:
The expressions enclosed within the
parentheses after assume are evaluated
first. If a tag is presented, the
object evaluated from the expression is attached to the tag, which is a
read-only variable. If the expression
does not evaluate to an attribute, parameter, variable, or an object name, a
tag must be provided.
The type of the expression is tested
against the AssumeType. If the test is successful, the statement
that follows the parentheses is executed.
Otherwise, if the else clause
is presented, the statement in that clause is executed.
Here’s an example:
x: Frame;
assume
(x is AnimateFrame)
{
x.ShowNextFrame();
}
The function ShowNextFrame is available only for an animation frame. If the
actual type is not checked, we cannot call this function through a variable
whose type is known as Frame only.
5.7.2 Inspect Statement
The inspect
statement is another multi-path
branch. It tests the type of a control
expression to see which set of statements should be executed.
The inspect
statement consists of the keyword inspect,
followed by a control expression enclosed in parentheses, followed by an
inspect body.
labeloptional
inspect (Tagoptional
Expression )
{
case
TypeCaseExpression : StatementList optional
case
TypeCaseExpression: StatementList optional
default
: StatementList optional
}