[SparForte][Banner]
[Top Main Menu] Intro | Tutorials | Reference | Packages | Examples | Contributors   [Back Page]      [Next Page]  

Script Tutorial 2: Intermediate Program Scripts

The first script tutorial showed how to write a basic script. This tutorial discusses how execute statements and delcarations can be organized. It also discusses how scripts can evolve and grow under SparForte.

Structuring a Script

Suppose you want to write a script that does something useful, such as email someone with a "bob" login when there are files waiting in a certain directory. In SparForte, the following commands can do the job:

num_files : integer := numerics.value( `ls -1 incomingdir | wc -l;` );
if num_files > 0 then
   /bin/echo "There are files waiting in the incoming directory" | mail -s "waiting files" bob;
end if;
 

Example: A Simple Unstructured Script

But consider the following questions:

  • How does the operating system know that this is a SparForte script?
  • Is there more than one "mail" command on the computer, which one executes?
  • How does the script inform the program if it succeeds or fails?
  • What happens if the ls command was accidentally deleted or altered by the system administrator (these things happen)?

SparForte scripts can be more than a list of commands. SparForte has a number of features that allow scripts to be "well-structured" (or modular). Well-structured scripts are easier to read and debug. Here's an example of a well-structured SparForte script:

#!/usr/local/bin/bush
 
pragma annotate( summary, "checkfiles.sp" );
pragma annotate( description, "check the files in the incoming directory and email someone when the files arrive." );
pragma annotate( author, "Ken O. Burtch" );
 
procedure checkfiles is
-- declarations
  num_files : integer;
  ls : constant command := "/bin/ls"; -- the ls command - man 1 ls
  mail : constant command := "/usr/bin/mail"; -- the mail command - man 1 ls
begin
 
-- commands begin here
 
  num_files : integer := numerics.value( `ls -1 incomingdir | wc -l;`);
  if num_files > 0 then
    /bin/echo "There are files waiting in the incoming directory" | mail -s "waiting files" bob;
  end if;
 
-- cleanup
 
  command_line.set_exit_status( 0 );
end checkfiles;
 

Example: A Simple Structured Script

The first part of this script is called the header. The header defines what kind of script this is, who wrote it, what version it is, and what restrictions or SparForte pragmas will apply to this script.

The very first line of a script is the header line. This line begins with a "#!" at the top of the script, flush with the left margin. This character combination identifies the kind of script. UNIX-based O/S uses this information to start the right program to run the script. For SparForte scripts, this line contains the absolute pathname to where the SparForte shell resides. On many systems, this will be /usr/local/bin/bush.

If you don't know the location of the SparForte shell, use the "whereis" command to find it:

=> whereis spar
bush: /usr/local/bin/spar
 

Example: How to Find the Interpreter's Path

The header line is followed by annotations describing the purpose of the script and who wrote it. This is important in case your script needs to be debugged in an emergency situation. The help command can read the annotations in scripts.

=> help checkfiles
Help for script checkfiles.sp:
 
Summary: checkfiles.sp
Description: check the files in the incoming directory and email someone when the files arrive.
Author: Ken O. Burtch

The main script is wrapped in a procedure statement. The procedure statement divides the script into two sections: declaring variables and commands to run. Putting all the declarations for a script in one place makes it easy for someone to refer to them while reading the script.

The commands are declared as command variables. A command variable is a special SparForte variable type: when a command variable is declared, SparForte verifies that the command exists and that it can be run. If the command cannot be run, SparForte stops the script before any command are executed.

Without command variables, SparForte searches several directories for the command you want to run. If it can't find it, the script will stop with an error after executing part of the script. In this circumstance, it is difficult to determine where the script left off and what needs to be done to continue. Instead, command variables and other "sanity checks" should be put at the top of the script, or in the variable declarations, to ensure that when the script fails because of these kind of errors that the system will be left in a consistent state.

After the main portion of the script runs (the part that does the actual work), the script should clean up after itself. Any open files should be closed or deleted and the script should return a status code to the person or program running the script. In this case, there are no files to clean up. All that is necessary is the set_exit_status command( 0 ) which indicates that the script ran successfully.

Main Procedures

A subprogram is a piece of a program that is given a name.  Subprograms can be functions, routines that return a value for an expression, or procedures that stand alone.  A SparForte script itself can be contained in a main procedure.

#!/usr/local/bin/bush
 
-- Bottles.sp: A Script implementing a children's song
 
procedure Bottles is
begin
  for X in reverse 1..99 loop
    ? strings.image( X ) & " bottles of beer on the wall";
    ? strings.image( X ) & " bottles of beer";
    ? "Take one down, pass it around";
    ? strings.image( X-1 ) & " bottles of beer on the wall" @ "";
  end loop;
end Bottles;
 

Example: A Script Enclosed in a Main Procedure Named "Bottles"

In this example, "Bottles" is the main procedure for the script. Variable declarations are placed between "is" and "begin" (in this case, there are none). Between the "begin" and "end" are the executable statements that the script will run.

Procedures and Functions

Additional procedures and functions can be declared in declared in the declaration section of the procedure.

#!/usr/local/bin/bush
 
-- math1.sp: do some simple math
 
procedure math1 is
 
procedure dotproduct is
  type vect3 is array(1..3) of integer;
  v1 : vect3 := (1,3,-5);
  v2 : vect3 := (4,-2,-1);
 
  sum : integer := 0;
begin
  for p in arrays.first( v1 )..arrays.last( v1 ) loop
    sum := @ + v1(p)*v2(p);
  end loop;
  ? sum;
end dotproduct;
 
begin
   dotproduct;
end math1;
 

Example: A Procedure Inside of a Main Procedure
 

#!/usr/local/bin/bush
 
-- math2.sp: do some simple math
 
procedure math2 is
 
  function double( n : integer ) return integer is
    -- multiply the parameter by two
  begin
    return 2*n;
  end double;
 
begin
  put_line( "5 doubled is" & strings.image( double( 5 ) ) );
end math2;
 

Example: A Function Inside of a Main Procedure

There is another kind of subprogram, a declare block, that is discussed in the reference section.

Debugging

The trace command will show each line of a script as SparForte reads it.

There are two pragmas for debugging.  pragma assert tests for a condition and stops the script if the condition is false.  pragma debug will run a backquoted command.  This debugging statements only run if SparForte is in debugging mode with the --debug option (or pragma debug with no parameters).  With these pragmas, you won't need to remove or comment out your debugging statements and risk changing your script's behavior.

  ten : integer := double( 5 );
  pragma assert( ten = 10 );
  pragma debug( `put_line( "the value of 10 is" & strings.image( ten ) );` );
 

Example: How To Use Pragma Assert and Debug

To perform more complex debugging, SparForte has a built-in debugger called the breakout mode. Using the breakout mode, you can stop a script at specific points, examine values and more. You can find out more about using the debugger in another tutorial.

See the Debugging Tutorial for more info

Standards Compliance and Portability

SparForte scripts can be copied or moved to other tools such as GCC (for binary applications), JGNAT (JVM applications) or A# (.Net applets).  Although tools like GCC understand most AdaScript features, they have no knowledge of Bourne shell commands or features only available on SparForte. The more commands, pipes and other shell features you use, the more difficult it will be to port your scripts to other tools.  At the same time, these special features make writing scripts easier.

GCC, JGNAT, A# and SparForte are all designed for compatibility with ISO-standard Ada. If your source code needs to be portable, use pragma ada_95. This pragma will disallow most of the Bourne shell features and will make your script as compatible as possible with the ISO-standard Ada language.  This does not mean that your script can no longer perform its job but you may have to rewrite the parts of your script using Ada-friendly features.  For example, you will no longer be able to use the "?" command, a SparForte-specific feature, but you can rewrite "?" commands to use put_line which is supported by GCC and other Ada- based tools. You lose the convenience of some features but gain source code reusability without a major rewrite of your work.

To further improve the portability of the script, you can turn off unnecessary features with pragma restriction.  For example, pragma restriction( no_external_commands ) will prevent any external operating system commands from running.

The final script looks like this.

-- math.sp: do some simple math
 
procedure math is
 
  pragma ada_95;
  pragma restriction( no_external_commands );
  pragma annotate( "This script performs some simple math" );
 
  function double( n : integer ) return integer is
    -- multiply the parameter by two
  begin
    return 2*n;
  end double;
 
  ten : integer := double( 5);
  pragma assert( ten = 10 );
 
begin
  put_line( "5 doubled is" & strings.image( ten ) );
end math;
 

Example: Our Finished Well-Structured Script

Retiring a Script

When a script becomes obsolete, use pragma deprecated (or depreciated) to specify name of the new script which makes the current script obsolete.  The old script will continue to run but when it completes a warning message will announce that the script is obsolete and SparForte will give the name of the new script.  In this way, old scripts can continue to run as programmers slowly upgrade to the new script.

[Right Submenu]

 Command Prompt Tutorial 1: SparForte as a Calculator

 Command Prompt Tutorial 2: Basic Shell Commands

 Command Prompt Tutorial 3: Working with Databases

 Script Tutorial 1: Basic Commands Scripts

 Script Tutorial 2: Intermediate Program Scripts

 Template Tutorial 1: Basic Templates

 Template Tutorial 2: Intermediate Templates

 GCC Tutorial: Compiling SparForte Scripts

 Debugging Tutorial - Using the SparForte Debugger

 Creating a Profile Script

 Calling SparForte from C: A Tutorial

 SparForte For PHP Developers

 SparForte Best Practices

[Back to Top] Back To Top [Small Forte Symbol]