Gavin Crawford
Icon

Ovation Pro Script Language Primer

Gavin Crawford explains the language basics that you need to know if you want to write scripts or applets for Ovation Pro.


This article is a companion to that on Writing Ovation Pro Applets.


This primer covers the following topics; click on a heading below to jump the associated section, and on the section heading to return to the contents list:


Ovation Pro's script language has many similarities with the C programming language, but for those unfamiliar with C, this section explains the basics of the script language. This section is primarily aimed at beginners, so explanations are intended to be as simple as possible. When describing some of the language's functions I will make direct comparisons with those of BBC Basic, as this is a popular language that many will have used. It should be noted that this primer should be read in conjunction with chapter 3 of the the Ovation Pro Script Language Reference Guide, which can be found as a document on the Ovation Pro CD (version 2.60 onwards). For convenience, a printed version is available from David Pilling. The StrongHelp version of the Reference Guide also includes the information.

 

Comments

Comments are a way of inserting remarks and notes into the code without affecting the program. Ovation Pro will ignore any comments when it processes the script file, so including comments won't affect the speed of your scripts once Ovation Pro has loaded and processed them.

There are two ways of adding comments. The first is the standard C-style comment, where the comments are enclosed within the two combinations of a slash and an asterisk, like so:

    /* an example comment */
This type of comment can span across multiple lines, and everything between the opening /* and the closing */ will be ignored. This can be handy if you wish to remove a whole section from your code temporarily while you are testing your applet, just by adding /* at the start of the section and */ at the end.
    /* this comment
    is spread across
    many lines, but all will be
    ignored */

The single-line C++ form is the second way of introducing a comment. This is entered as two slash characters followed by the comment, which continues until the end of the line:

    // a single line comment
This single-line comment is very similar to Basic's REM statement.

One last thing to note is that comments can be on the same line as other statements. Just remember that the single-line version starts with // and continues up to the end of the line, unlike the C-style comments, which can have statements on the same line that follow after the closing */.

 

Making statements

When we write an executable statement in our script, such as version=programversion();, it is followed by a semicolon. The semicolon tells Ovation Pro that the statement is complete and marks the end of the statement or instruction. (Incidentally, the function programversion(); returns Ovation Pro's version number.) Unlike Basic, which would take a Return character as the end of a statement, Ovation Pro's script language behaves like C, in that a single statement can span multiple lines until it reaches a semicolon. For example, the following code performs exactly the same function but can be written in two different ways:

    type("Ovation Pro");  /* a complete statement all on one line */
    
    type                  /* This does the same */
      (                   /* job, but is        */
         "Ovation Pro"    /* written            */
            )             /* across five        */
    ;                     /* lines              */

The first is how you would normally write the statement; the second shows how extra lines are permitted in the script language. Also notice that the extra spaces inserted between each element of the statement have no effect on how the code performs its required task. The important thing to note, though, is that you cannot insert Return characters or spaces into the middle of any of the separate elements, as you are likely to get an error if Ovation Pro misinterprets what you have written. In other words, if you attempt to break the word type onto two lines, between the 'y' and the 'p', Ovation Pro will produce an error when it comes across the word 'ty'.

Although the second part of the above code works when it is written across five lines, writing the statement like this isn't particularly good practice as it makes it harder for us humans to read and understand it, but it does have a use when you need to write long statements that won't fit on one line in your editor's window; for example:

    open_window(window_handle,
                window_wx0,
                window_wy0,
                window_wx1,
                window_wy1,
                window_wscx,
                window_wscy,
                window_bhan);

 

Variables

A variable is an area of memory used to store data. We give the area a name, and when we need to use the data we can supply the name and the program will fetch the data from the area. The name (or identifier) is a sequence of letters and digits and can be of any length, but the first character must be a letter or an underscore character. Both upper- and lower-case letters are acceptable, but it seems to be common convention that only lower-case letters are used in Ovation Pro applets and scripts.

Reserved keywords, which are discussed later, cannot be used as identifiers.

A variable also needs to be given a type. The type is used to determine what kind of data can be stored in that variable. This is similar to Basic, which uses $ to represent a string variable and % for integer variables.

Ovation Pro supports three types of variables:

  • int (32-bit signed integers)
  • string (variable length character strings)
  • void

Before a variable can be used it must first be declared. This is done by writing the type of variable followed by the variable's name, or a comma-separated list of variable names which are to be treated as being of that type:

    int i,loop,view;
    int file;
    string applet_name;

Once the variable has been declared it can be assigned a value using the equals character:

    applet_name="Test Applet";

A variable has to be declared in this way before it can be used in the program, so you couldn't enter the above example without first declaring it with a type, although variables can be assigned values at the same time as they are declared, which is often referred to as 'initialisation of variables':

    int value=10;
    string text="My Applet";

The place where the variables are declared is important, as there are only three acceptable places where declarations can be made. Where a variable is defined determines whether it is global or local. This is referred to as the variable's scope.

 

Global variables

The first possible place to declare variables is outside all the functions; that is, in the space between function definitions. However, it is common practice to group all such declarations at the start of the code, usually just after any comments that describe the program. When a variable is declared outside functions, it is called a global variable, which means that its value will be retained between uses and can be accessed by any functions, including functions in other scripts. The important point to note about global variables is that you cannot have two global variables with the same name. That includes any that are declared in other script files, applets or even Ovation Pro itself. To avoid name clashes, the adopted approach is to precede the name with the name of your applet and an underscore character:

    int myapplet_globalvar;
    string testapplet_hotkey="CS_#"
    int nudgepal_centre=1;

 

Local/automatic variables

Anther place where variables can be declared is inside compound statements, which are 'blocks' of statements that are grouped up using { } braces. The variable declarations must appear before any other statements; in other words, directly after the opening { character, although you could have comments in between. When they are declared within braces they are referred to as local or automatic variables. This is because they are created only when the opening brace is encountered and they are discarded automatically when the closing brace is executed, or when control jumps out of the block. They are only usable from inside that block, and therefore there is no need to worry about two local variables having the same name if they are in different blocks. You would use local variables when the variable is not going to be needed outside a particular block. For instance, if you have a loop counter then, when the count is over, the counter variable is no longer needed and can be discarded.

The final position where a variable can be declared is inside the parameter list of a function definition, between the ( ) parentheses. Again, these become local variables. This will be covered in more detail in the section on Functions.

 

Data types


int

The int data type is a 32-bit signed integer. This is a whole number in the range of −2,147,483,648 to 2,147,483,647. This is similar to BBC Basic's use of the % character when creating an integer variable. The space allocated to store an int is four bytes, which is the same as one 32-bit word.

You can assign a value to an int variable using the equals character; for example:

    int loop=16;
    int length=255;

In the above example, the variable called loop has been declared and at the same time it has been assigned with a sequence of digits representing the value of 16 in decimal. To assign numbers in hexadecimal (base 16), the sequence of digits is preceded by 0x or 0X (zero, letter-X):

    int loop=0x10;
    int length=0xff;

Octal numbers (base 8) can be entered by preceding the sequence with 0 (zero). Be aware of this, in case you should be tempted to 'pad' a decimal number with preceding zeros.

 

string

The string data type is much the same as Basic's in that a sequence of characters enclosed by double-quotes can be assigned to a variable, again by using the equals character:

    text="Ovation Pro";

There are, however, some notable differences between Basic's handling of strings and that of Ovation Pro. Firstly, a null byte is appended to strings to terminate them, as in C, so that programs that scan the string can find their ends. The other major difference is that there is no limit to the length of a string, unlike in Basic, where strings cannot exceed 255 characters in length.

Within a string you can include escape sequences, which are codes that represent special characters. The escape sequence is started with a backslash followed by another character:

Escape Character ASCII
\r carriage return 13
\n new line 10
\t horizontal tab 9
\" double quote 34
\\ backslash 92

So, to expand on the previous example above, we could extend the string to be:

    text="Ovation Pro\nTHE PROFESSIONAL DESKTOP\nPUBLISHER\r";

This now includes two new-line characters and a carriage return at the end to create a new paragraph.

There are other special sequences that can be used in a string, details of which can be found on page 7 of the Ovation Pro Script Language Reference Guide, and also in the StrongHelp version.

One final note about strings: when using the functions in the script language that operate on the characters of a string, the position of characters is counted from 0, not from 1 as in Basic.

 

void

The void type is a bit different from int and string, as you wouldn't actually declare a variable as type void. It is used to discard the value. It does have a valid use, when used with functions, as we shall see later.

 

Reserved keywords

The following is a list of keywords that are reserved; you cannot use them as variable names or names for your functions:

    break else string case for switch continue if void default int while do return

These words are reserved because they form the commands that make up Ovation Pro's script language. The uses of these keywords will be described in other sections.

 

Functions

Functions are an important aspect of any program design, as they allow us to group together a collection of instructions or statements that can be executed whenever we need them. Each function is given a name, so when we need to use our group of instructions we can just call the function's name and the program will jump to the start of the function and work its way through the statements. When it reaches the end of the function it will then jump back to where it was before.

A function has to be defined in a particular way and the basic layout of a function's definition looks like this:

    type identifier(parameter1,parameter2)
    {
      ...
    }

Braking this down, we can see four parts: the type, the identifier, the parameter list between the parentheses, and finally the { } braces.

The identifier is simply the function's name. The conventions of naming functions are identical to those of variables as mentioned above. In other words, you can use a sequence of letters and digits of any length, but the first character must be a letter or an underscore character. Upper- and lower-case letters are permissible, but again it is common that only lower-case letters are used in Ovation Pro scripts. As with variables, functions must have a unique name that has not previously been used in your script or in any others that Ovation Pro has loaded. Also note that functions and global variables cannot share the same name. To avoid name clashes, the same approach should be adopted as used with global variables: the function name is usually preceded by the applet name and an underscore character, in the form appletname_functionname.

There is one special exception to the rule about unique names, and that is a function called main(). Every script file must have a function called main() if Ovation Pro is to execute any statements in the file. This function is executed first when Ovation Pro runs the script file. It is from inside the main() function that you would call other functions. This is similar behaviour to a standard program written in C.

The type of a function tells Ovation Pro what sort of result the function will return. A function can be made to hand back a value to the place at which it was called. The kind of value it passes back is constructed as part of the function's definition: it can be an int, a string or a void. Every function must be defined as having a type, but what if we don't need a value to be passed back? Well, this is where the void type comes in, as it means that the value can be discarded and ignored. A void function, then, is like a procedure in Basic, in that it jumps to the start of the function, executes the statements in the block and exits back to where it was called from.

To pass a value back from the function, we use the keyword return. You can only pass back the data type specified in the function definition; in other words, if the function has been defined as having an int type, then you cannot return anything other than an int.

    int example_function(void)
    {
      int a=2;
      int b=4;
      return a+b;
    }

This example shows a function that creates two local integer variables and passes back the sum of the two variables as a number. Notice that the function starts by declaring that it is an int type function, and therefore the returned data being passed back must also be an integer. A function could equally be defined as a string type, and in that case it must pass back a string.

The function's parameter list defines what types of data the function is expected to be given. We use this to pass data into a function for it to work with. When we define the function, the parameters that it requires are placed inside the parentheses. If the function doesn't require any parameters, we place void inside the parentheses. If, however, the function does require parameters to be passed to it, we must declare the type of every parameter that the function requires. Each parameter can be of type either int or string, and a variable name must be given for each parameter.

    int multiply_by_two(int value)
    {
      return value*2;
    }

We can see above that when this function is called it requires an integer to be passed to it. When called, the program jumps to the start of the function, and a local variable is automatically declared with the name value. This local variable is assigned the value that was passed to the function, so if we wanted to pass the number 8 to the function we would use multiply_by_two(8); and the variable value would now contain 8. In this example, the rest of the function just returns back our value multiplied by 2.

If we require a function to have more than one parameter then we add more parameters between the parentheses using commas between each parameter. So, going back to our previous example that added a+b, we could improve the function by defining parameters for a and b so that their values would be passed to the function and the sum would be returned:

    int add_together(int a, int b)
    {
      return a+b;
    }

You should take note that the two variables that are created inside our function, called a and b, are local only to the function and will be discarded when the function returns. It is therefore perfectly acceptable to use simple names for the parameters, as there won't be a problem with clashing names in other functions. When the local variables are created, they will be assigned with a copy of the value passed. So, if we pass to a function a variable, such as add_together(size,10);, the function takes a copy of the variable's value, but it cannot alter the variable directly; it can only alter its own private local copy. This is referred to as call by value.

A function can be defined so that it can alter the variable's value directly. This is known as call by reference, but it can only be used with string parameters. Call by reference is used if an ampersand character (&) is placed before a string parameter in the function's definition. What actually happens with call by reference is the memory address of the string variable is passed to the function so that it can use the string directly without taking a copy. The function can then modify the string variable's data directly. Many of Ovation Pro's resident functions use this technique, and you will come across numerous functions listed in the Script Language Reference Guide that show an ampersand in the function's description.

The actual statements and instructions that we want the function to perform are placed between the opening and closing braces, { }. If for any reason we need to exit out of a function before we reach the closing brace, we can use the return command. We saw this command earlier, when we used it to pass values back from our function to the code that called it. As before, we can exit at any time and pass back a value at the same time. For a function that has been defined as type void, and is therefore not expected to return a value, the return command can be used on its own, to exit out of the function.

Warning note

Ovation Pro doesn't support function prototypes, so you can only use a function after it has been defined earlier in your code. This can mean that, as your scripts get bigger, you may have to rearrange the order of your function definitions if you find that you need to call functions that are defined lower down. This is very different from programming in Basic, where code inside one function can call another function no matter the order in which the functions are defined.

 

Statements

In the following section we will look at the different statements that we can use in our scripts. I will also provide a comparison to statements in BBC Basic.

 

Loops

 

while loop

A while loop is used to execute repeatedly a statement, or a block of statements, as long as the value of the expression remains true (or, more specifically, as long as the expression does not evaluate as zero). The full construction of the while loop looks like this:

    while ( expression ) statement;

The expression part in parentheses is evaluated before execution of the statement, and so must be true (not zero) in order for the statement to be processed.

If you want the loop to repeat multiple statements, then braces must be used to enclose the statements, as follows:

    while ( expression ) { statement1; statement2; statement3; }

But most often these statements would be written on separate lines to aid readability of the code:

    while ( expression )
    {
      statement1;
      statement2;
      statement3;
    }

This form of grouping multiple statements within a set of braces is used for all the other forms of loops in exactly the same way.

As a comparison, the while loop here is similar to the WHILE ... ENDWHILE form as used in BBC Basic.

 

do loop

A do loop is similar to the while loop above, except that the expression is evaluated after the statement is executed, meaning that you will always get at least one pass through the loop, even if the expression is false when it is first checked. The layout looks like this:

    do statement; while ( expression );
or, in order to repeat multiple statements, it would again need braces, like this:
    do
    {
      statement1;
      statement2;
      statement3;
    } while ( expression );

This is similar to the REPEAT ... UNTIL loop in BBC Basic, except that in Basic the loop repeats until the condition is true, whereas here the loop is repeated while the expression is true.

 

for loop

A for loop is often used when you need to repeat a statement (or a set of statements) for a particular number of times. The form of a for loop looks like this:

    for ( statement1; condition; statement2; )
    {
      // statements to be executed in the loop
    }

The loop will normally have a variable that is associated with the loop and is used to control how many times the loop will be executed. statement1 is an expression to initialise the controlling variable and usually takes the form of i=1;, for example, and is only executed once before the start of the loop. The condition part is evaluated before each time the loop is about to be repeated, and the loop is only carried out if the expression is true. It will often look something like this: i<=5;. statement2 is an expression, and is used to alter the value of the controlling variable. This usually means adding to the controlling variable if the loop is counting up, or subtracting from the variable if counting down. Often, this would take the form of i++; or i--; to increment or decrement by one, but it could equally be something like i+=5; to count up in steps of 5.

As an example, in order to create a loop that will repeat the statement five times we would use the following:

    int i;
    for ( i=1; i<=5; i++ )
    {
      type("loop number: " + itos(i) + "\n" );
    }

We first have to declare a variable, in this case i, to use as the loop counter. This will have to be declared using the normal rules that govern variable declarations (at the top of the function or as a global variable). Then we have statement1 that sets i to 1. Next is the condition that checks if the value of i is less than or equal to five, and if so it executes the statements within the braces. After the block of statements in the braces have been executed, i is incremented by one. This is done in statement2 with i++.

The results of this example would look like this:

    loop number: 1
    loop number: 2
    loop number: 3
    loop number: 4
    loop number: 5

 

break and continue

The break statement can be used in the above loops to jump out of the loop. When the statement is encountered, the loop is exited immediately. The break statement can also be used in conditional constructs.

As well as jumping out of a loop, there may be times when we need to hurry the loop on to the next pass, maybe to avoid having to go through other code that is not needed. For this we use the continue statement, which causes control to pass to the start of the loop on the next pass.

 

Conditional constructs

There are times when our code will have to make decisions to execute some statements based on certain conditions.

 

if statements

The if statement will evaluate an expression and, if the outcome is true, will execute a statement (or statements).

The if statement here is very similar to its equivalent in BBC Basic except that we don't use a THEN, and the expression must be enclosed in parentheses. In its simplest form it looks like this:

    if (expression) statement;

This will execute the statement only if the expression evaluates as true. If we wanted more than one statement to be executed when our expression is true then we would use braces to form a block of statements, like this:

    if (expression)
    {
      statement1;
      statement2;
      statement3;
    }

On the occasions when we want our code to do something if the expression is true, but do something else if it's not true, then we use else:

    if (something_is_true) do_something; else do_something_else;

And for multiple statements:

    if (something_is_true)
    {
      do_something;
      and_this;
    }
    else
    {
      do_something_else;
      followed_by_this;
    }

 

switch

The Ovation Pro Script Language Reference Guide explains the switch statement as follows:

The switch statement causes control to be transferred to the case statement which matches the value of the expression. There may be one default statement to which control is transferred if no case constant matches the expression.

It shows its form as:

    switch ( expression )
    {
      case int_constant: statement;
                         break;
    
      default:           statement;
                         break;
    }

Using the switch construct allows us to take different paths through our code in a similar way as writing lots of if ... else statements. It is similar to BBC Basic's CASE statement.

An extended explanation of the switch statement is as follows. The expression is evaluated. The program then checks to see whether the result of that expression matches any of the constants labelled with case. If a match is made then execution will start just after that case statement, and will carry on until either the closing brace is encountered or a break statement is found. break is a useful way of exiting out of the switch block. If no match is found then the statements following default (if present) will be executed.

You should note the following, as it is often something that can catch you out when you first start using the switch construct. When a match is found and execution starts from that matching case, it will continue to 'fall through' all of the following case statements, and any statements set as default, unless it finds a break. So it has a different behaviour from BBC Basic's CASE command, which will only execute the single matching WHEN.


This article was originally published in Foundation RISC User magazine, and is reproduced here with the permission of Richard Hallas.
All text and code Copyright © Gavin Crawford, 2006