Visviva Player | Visviva Tools | SDK for Developers | ScriptV

Overview Tutorial

ScriptV Language Reference

1. Lexical Conventions

This chapter describes the lexicon of ScriptV.  Contained in this chapter are all of the alphanumeric characters used in the ScriptV language, as well as the ways these characters are combined to form the "Lexical Elements" that ScriptV recognizes as legal programming instructions.

1.1.    Character Set

Below are the characters that are used to write ScriptV programs.  The sum total of all the characters that can be used to write a source code is called a Character Set.  A ScriptV source is a sequence of characters selected from this character set.  A ScriptV interpreter may use any character set, as long as it includes the following standard characters that constitute the lexical elements of ScriptV.

An AlphabeticCharacter is one of the following:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

a b c d e f g h i j k l m n o p q r s t u v w x y z

A DecimalDigit is one of the following:

0 1 2 3 4 5 6 7 8 9

This is an Underscore:

_

Separators: refers to one of

{ } ( ) [ ] , ; ' " #

An Operator Character is one of the following:

! # $ % ^ & * - + = ~ \ [ ] | : . < > / ? @

A WhiteSpace can be one or more of the following:

blank

horizontal or vertical tab

newline

formfeed

comment

1.2.    Comments

A comment is a piece of text that serves to explain certain lines of code.  This code explanation is solely for the edification of the reader and does not contribute any meaning to a ScriptV program.

A comment in a ScriptV program begins with the characters /* and ends with a matched */.

A comment can also begin with the characters // and end with the first subsequent occurrence of a new line.

Comments enclosed by /* and */ can be nested.

Here are some examples of ScriptV comments:

object xyz /* comment: xyz is an object name */

       is XYZ  // comment: XYZ is a class name

{

       /* comment: here is the body of the object

             /* with a nested comment here */

       */

}

1.3.    Lexical Elements

The ScriptV character set may be grouped into four different types of lexical elements:

Identifiers:                A continuous sequence of characters used as names for objects, classes, attributes, functions, etc.

Keywords:                 A continuous sequence of characters reserved for lexical elements that have specific meanings in ScriptV.

Operator strings:     A continuous sequence of operator characters used for specific function names, such as +, -, *, /, and ++.

Literals:                     The fundamental representation of the value of an object, such as an object's integer value, floating-point number value, character string, etc.

 

How to Separate Lexical Elements:

Lexical elements are most commonly separated by white spaces.  For instance, the following text

object xyz is XYZ

contains four lexical elements, each separated from the next by a blank space.

Separator characters can also separate lexical elements.  For example:

x,y

The character sequence "x,y" is separated by a comma separator, and has three lexical elements: "x", ",", and "y".

Unlike white spaces, separators are lexical elements themselves.  As such, they have associated semantics.  Separators may be used to introduce a new scope in context.  Separators may also be used to change the priority in an expression’s associativity, separate parallel grammatical elements, constitute an operator string, and so forth.  Some separators must appear in pairs (like { }, [ ], etc.).

Each separator character is a lexical element that cannot be used in combination with other characters.  For example, the following string:

,,,

contains three separate lexical elements.  These elements do not combine into a single lexical element that has three commas.

 

 The four lexical elements are discussed in detail in the following sections.

1.3.1   Identifiers

An identifier is an arbitrarily long continuous sequence of alphabetic characters (a, b, c…), underscore symbols ( _ ), and decimal digits (1, 2, 3…). Identifiers are names that distinguish individual entities in ScriptV programs.  Identifiers name such program entities as objects, classes, and functions. 

The first character in an identifier must not be a decimal digit.  Though an underscore symbol can be used as the first character in an identifier, this is not a recommended procedure.  Identifiers with their first character as an underscore already have a specific usage as names for system entities

All alphabetic characters are significant in ScriptV.  Upper- and lower- case letters are different and will be read by ScriptV as such.  Therefore:

name

Name

NAME

are three different identifiers.

Here are some more examples of identifiers:

Sphere

sphere1

sphere_color

The following are not identifiers:

3D_Point

Percent

Laüfer

Identifiers are used internally by the computer to distinguish the names of objects, classes and functions.  The ScriptV identifiers exclude extended characters such as and ü.  The Visviva Animation Engine supports a translation mechanism through which object and class names can be viewed externally in different national languages.  Using extended characters internally can bring about problems in creating language-independent animation titles and cause confusion in releasing, reusing, and exchanging works internationally.  For this reason, identifier names are restricted to standard alphabetical characters.

1.3.2   Keywords

A keyword is a continuous sequence of characters reserved for lexical elements with specific ScriptV meanings.  They may not be used otherwise.  Following is a list of ScriptV keywords, some of which have already appeared in this book:


assume

at

begin

break

case

class

const

continue

default

do

else

end

enter

exit

for

foreach

func

if

in

inspect

is

null

object

of

operator

owner

prefix

persistent

return

root

self

sub

super

switch

type

while

with

and

or

not

has


The following words, although not currently used as keywords in ScriptV, may become keywords in the compiled version of this language and so are reserved for future use.


abstract

assert

by

def

deferred

exact

fixed

final

imp

interface

meta

noperator

private

protected

public

retry

sealed

throw

try

when

use


1.3.3   Operator strings

An operator string is an arbitrarily long continuous sequence of operator characters.  An operator string is used as a name, or as a part of a function’s name that distinguishes one function from another.  A function can be named with an identifier or with operators. ScriptV supports user-defined operators. 

Under no circumstances may a user-defined operator string contain any of the following character combinations:


/*

//

*/

:<

=. 

:. 

<. 

>. 

+. 

-. 

*. 

/. 

%. 

|. 

&. 

~. 

!. 

^. 

?.

.= 

.: 

.< 

.> 

.+ 

.-

.* 

./ 

.% 

.|

.& 

.~ 

.! 

.^ 

.?

 


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

}