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

Fundamental Types

All AdaScript variables have a type.  Variables must declared so that SparForte knows what that type is.  When new variables are created by assignment at the command prompt, SparForte choses an appropriate type for the new variable.  In scripts, all variables must be declared.


[SparForte Type Tree]
 
Figure: SparForte Predefined Scalar Data Types and their Relationships


Identifier Names

Identifiers must begin with a letter and may contain letters, numbers and underscores. There is no minimum or maximum length to identifiers.

Style Restrictions on Identifier Names

SparForte imposes style restrictions on identifier names. These restrictions are for names that are technically acceptable but are not practical in real scripts. Some of these restrictions only apply to certain contexts.

  • The lowercase "l" and uppercase "o" cannot be used as identifier names because they can be confused with the numbers zero and one. This only applies to scripts: they are permitted on the command line.
  • Identifier names that are equivalent to reserved words except for the letter case are not permitted. For example, "IF" is not permitted. This is done to prevent confusion, as well as to improve Ada compatibility.
  • Variables or constants with ambiguous, meaningless names in English like "stuff", "data or "thing" are not allowed. This is done to promote descriptive names. This only applies to scripts: they are permitted on the command line.
  • Subprogram names contain at least 3 characters. This is to make the names descriptive.
  • Program names that may conflict with built-in Bourne shell commands are not allowed, such as "eval", "exec" or "test". For example, if you name a program "test" and try to run it in a Bourne shell by typing "test", you'll run the built-in shell command instead of your program. This prevents such confusion.

Implementation Rationale: As much as possible, only "common sense" style issues are implemented. Some issues enforced by other languages or tools do not have evidence that they are effective: for example, single letter variables are often prohibited, but variables like "x" and "y" are commonly used in computer graphics. Usually the restrictions are removed for the command prompt, where style is less important.

Universal Types

There are three "universal types":

Type Name Content Usage
universal_string unlimited length, 8-bit characters text
universal_numeric double precision floating point values numbers
universal_typeless same as universal_string/numeric text or numbers

The first two types form the basis for all other AdaScript string and numeric types.  Variables declared as universal_typeless change their type between universal_string and universal_numeric depending how they are used.  If an enumerated value is assigned to a univeral_typeless variable, it becomes a number with the ordinal value of the enumerated item. If AdaScript cannot decide in a particular context, the variable type defaults to universal_string.  Typeless variables correspond to variables in Bourne shells and are provided for as a command line convenience.

Universal types automatically match any type derived from the same universal type. A universal_numeric variable accepts integer or floating point values.  A universal_string variable accepts single characters or strings containing many characters.

Universal types are used for all AdaScript literals.  For example, a string literal like "hello" is a universal_string and be assigned to any string type. The numeric literal 45.5 is a universal_numeric can be used with float, long_float, or any other numeric type.

Using these three built-in types will give you a minimum amount of type checking, suitable for short scripts or quick command line calculations.  Universal types should not be used in large scripts because they will greatly reduce the number of errors SparForte can detect.

Variables are declared as name, a colon and the type. More than one name can be used in a comma list to declare several variables of the same time at one time.

=> i : universal_numeric;
=> j, k : universal_string;

Ada: Ada has no usable universal types but the Ada rationale uses universal types to describe the type system.

Predefined, Non-Universal Types

For more extensive scripts, AdaScript extends the universal string and numeric types into all the predefined Ada language types, plus some AdaScript-specific types:

Type Name Base Type Usage GCC Ada Equivalent
integer universal_numeric number without a radix point integer
natural universal_numeric 0 or integer larger than zero natural
positive universal_numeric integer larger than zero positive
short_short_integer universal_numeric very small integer short_short_integer
short_integer universal_numeric small integer long_integer
long_integer universal_numeric large integer long_long_integer
long_long_integer universal_numeric very large integer float
float universal_numeric floating point number float
short_float universal_numeric small floating point number short_float
long_float universal_numeric large floating point number long_float
character universal_string 8-bit character character
string universal_string unlimited length string almost unbounded_string
unbounded_string universal_string unlimited length string almost unbounded_string
json_string string unlimited length string for JSON encoded data almost unbounded_string
duration universal_numeric time, float seconds duration
boolean enumerated type true or false boolean
file_mode enumerated type in_file, out_file or append_file file_mode
command limited type alias for an operating system command  -
file_type limited record type operating system files file_type
socket_type limited record type operating system sockets -
complex record type complex numbers complex

The built-in packages may define additional types.

By default, all numeric variables are initialized without any value. Any attempt to use uninitialized numeric variables in an expression will cause an error or exception.

=> i : integer -- no value specified
=> i := i + 1
i := i + 1;
          ^ exception raised

Most types are incompatible with each other. Types may be typecast into related types.

=> i : integer := 5
=> f : float := 0
=> f := i
f := i;
      ^ type float is not compatible with type integer
=> f := float(i) -- typecast
=> ? f
5

You cannot typecast a numeric type into a string type directly.  There are functions in the numerics and strings packages to do these conversions.

Integer, Natural and Positive

Natural and positive are technically subranges of the integer type. Positive is an integer with positive values. Natural is an integer with zero or a positive value. SparForte does not support subranges. This creates a problem: if they are all incompatible types, then typecasts will be needed whenever they are used. If they are all subtypes, then the benefits of strong typing are lost.

Before SparForte 1.4, natural, positive and integer were defined as distinct types. After SparForte 1.4., natural and positive are subtypes of integer. You can mix natural, positive and integer in expressions, assignments and parameters. However, as a special case, SparForte will check the values of natural and positive when assigned to a variable, type cast or used as a parameter. This run-time check only occurs for natural and positive types, not types derived from natural or positive.

=> p : positive := 1
=> n : natural := 0
=> n := p
=> n := 0
=> p := n
p := n;
      ^ positive value is less than zero
=> i : integer := positive( n )
i : integer := positive( n );
                            ^ positive value is less than zero

Ada: natural and positive are subranges of integers. That is, Ada sees them all the same except there are range restrictions on acceptable values. Arithmetic is done by treating them all as integers. The range of acceptable values is only checked when a value is assigned to a variable or parameter. SparForte cannot do this because it doesn't have subranges.

C/C++/Java: A natural is not the same as an unsigned type. A natural is an integer with a restricted set of values and the sign bit is not used for storing values.

Type Attributes

The System package contains information about the representation of types on your computer. You can use this package to determine the minimum integer, or the precision of a floating-point number. Array properties can be discovered with the arrays package. In this case, this is a 64-bit computer (with 8-bit bytes) and it has a maximum integer of 999,999,999,999,999.

? System.Min_Int
-999999999999999
=> ? System.Max_Int
 999999999999999
=> ? System.Max_Mantissa
63
=> ? System.Max_Binary_Modulus
 1.84467440737095516E+19
=> ? System.Max_Nonbinary_Modulus
 4294967295
=> ? System.Fine_Delta
 1.08420217248550443E-19
=> ? System.Storage_Unit
 8
=> ? System.Word_Size
 64
=> type my_array is array( 1..20 ) of integer
=> ? arrays.length( my_array )
 20
=> ? arrays.first( my_array )
 1
=> ? arrays.last( my_array )
 20

See the description of the System package for more information.

String Gotchas

Since all literals have a universal type, this can cause some unusual side-effects. A character variable can contain more than one character if you really want it to by assigning a string literal. Characters are stored as internally as a string and a string literal is a universal_string type.  AdaScript will allow the assignment.  However, the type checking will prevent a character variable from being assigned to a string variable.

=> i : integer := 5
=> c : character;
=> c := "hello"; -- confusing, perhaps stupid, but legal
=> s : string := c; -- ILLEGAL
                  ^ type string is not compatible with type character
=> c := character( "hello" ); -- explicity typecast is not fooled
c := character( "hello" );
                         ^ character value must be one character long

In this example, "hello" is a literal so it is a universal_string. The variable c is a character, a type derived from universal_string. So SparForte allows the assignment. Internally, SparForte uses strings for characters so the entire value of "hello" is assigned to c and c is now a character variable with 5 characters in it. However, SparForte's type rules prevent c from being assigned to a string, and typecasting will check the length of a character variable. (This will likely get addressed in the future.)

AdaScript strings are an unbounded string type--that is, they are stored as an Ada.Strings.Unbounded.Unbounded_String variable.  They have an unlimited length and are not implemented as any sort of array. Ada "fixed" strings, the default string type for GCC Ada, are not used because they have limited length and can create array bounds exceptions. Strings are an integral part of scripts and unbounded strings are used instead. AdaScript includes an unbounded_string type (it has the same properties as the string type) which may make porting to GCC Ada easier.

Constants

Constants can be declared with the word "constant" for any type. The use of "constant" doesn't not affect the type of the variable--it simply prevents new values from being assigned by making the variable "read-only".

program_name : constant string := "Nightly FTP Transfer";

Limited Types

file_type and socket_type variables are known as limited type variables. Limited types cannot be assigned a new value with an assignment statement

=> f : file_type
=> g : file_type
=> f := g
f := g;
      ^ limited variables cannot be assigned a value

SparForte manages the contents of these variables and scripts are not allowed to change the contents.

Complex Types

Variables declared complex represent complex numbers.  A complex number is a record with long_float real and imaginary parts.  The real field is designated "re" and the imaginary field is designated "im".  (See below for more information about using records.)

=> c : complex := ( 1.0, 2.0)
=> ? c.re
 1.0
=> ? c.im
 2.0
=> c.re := 5.0
=> ? c.re
 5.0
=> numerics.set_re( c, 3.0 )
=> ? c.re
 3.0

Some of the numerics package subprograms work with complex types.

Complex types are not fully implemented in this version of SparForte: some of the Ada complex number functions are not available.

Command Types

External operating system commands can be declared using command variables. When a command is declared, SparForte will ensure that the command exists and is runnable.

Command types are similar to limited types and have several restrictions.

  • they must be declared as constants
  • they cannot be assigned to other variables
  • they cannot be used in expressions (although they can appear by themselves and are treated as strings)

=> l : constant command := "/bin/ls"
=> m : constant command := "/bin/lt"
m : constant command := "/bin/lt";
                                 ^ "/bin/lt" is not an executable command
=> n : constant command := l;
n : constant command := l;
                         ^ type universal_string is inherently different from a command
=> ? l & " is the path"
? l & " is the path";
                    ^ type command is inherently different from a universal_string
=> ? l
/bin/ls

JSON and SparForte Types

JSON is an acronym for JavaScript Object Notation. It is an easier-to-use and more limited standard for data interchange compared to XML. Many programming languages support JSON.

SparForte contains several functions to convert variable values into JSON, and JSON into variable values. JSON doesn't support enumerated types, so SparForte converts enumerated types into integer, the ordinal position of the enumerated values. Records are coverted into JSON objects.

If an array or record has a json_string component within it, the content of the json_string is assumed to be JSON data and will be added as-is to the json-encoded aggregate. Use json_string to build up more complex JSON structures like arrays of arrays.

Currently, SparForte doesn't support UTF-8. It doesn't support characters larger than 8 bits. Encoding arbitrary control characters with \u is not yet supported. Carriage returns, line feeds, and other backslash escapes are supported.

JSON type SparForte type
boolean boolean
number numeric or non-boolean enumerated types
string string types
array array
object record

Exceptions are raised for a number of conditions:

  • Exception is raised if the number of components don't match

  • Exception is raised if a string value is given to a numeric array, or a numeric value is given to a string array.

  • Exception is raised if the an enumerated ordinal value is out of range

  • Exception is raised if the a boolean value is not true or false

type a_sample_enum is ( e1, e2 , e3 );
type an_example_record is record
   s : string;
   i : integer;
   b : boolean;
   e : a_sample_enum;
end record;

r : an_example_record;
r.s := "aardvark";
r.i := 54321;
r.b := true;
r.e := e2;

js : json_string;
records.to_json( js, r );
? js; -- displays {"s":"aardvark","i": 54321,"b":true,"e": 1}
 

Example: Encoding a record as a JSON string

<?php
$r = json_decode( '{"s":"aardvark","i": 54321,"b":true,"e": 1}' );
var_dump( $r );

/* displays this:
object(stdClass)#1 (4) {
["s"]=>
string(8) "aardvark"
["i"]=>
int(54321)
["b"]=>
bool(true)
["e"]=>
int(1)
}
*/
 

Example: Deccoding JSON string into a PHP Object
 

Renamings

A renaming is a second name (or alias) for a variable. Both the renaming variable and the original (or canonical) variable share the same value.

Variables renaming other variables can be done for scalar variables or constants, command constants, arrays, array elements, records, record fields and enumerated variables. An exception cannot be renamed.

=> this_month : string := "October"
=> m : string renames this_month
 ? m
=> October
 type fruit_array is array(1..4) of fruit
=> fruits : fruit_array := (berry, cherry, apple, apple)
=> the_cherry : fruit renames fruits(2)
=> ? the_cherry
=> cherry

The type must be identical, or subtypes of each other. A universal_typeless variable must be renamed by another universal_typeless, for example. Likewise, a constant must be renamed by another constant.

Values can be assigned to the renaming variable (if the variable is not a constant, of course). The original variable's value will also change.

A renaming of a variable can be a constant, creating a read-only renaming.

=> q : constant character renames Latin_1.Quotation
? q
"

There can be multiple renamings on one identifier, or you can have renamings of renamings.

=> title := "My Report"
=> (Assuming title is a new universal_string variable)
=> t1 : string renames title
=> t2 : string renames t1
=> ? t2
My Report

Commands that alter variables like "unset" and "typeset" will not work on variables referred to by existing renamings. The renamings must be removed first.

The "env" command, when used on a renaming, will show both the renaming and the original variable.

Types can be renamed with the subtype statement.

Ada: A constant renaming is not permitted in Ada. When renaming a constant in Ada, the keyword "constant" is not used. Since SparForte doesn't implement value ranges for subtypes, natural and positive, subtypes of integer, are equivalent to integer for renaming purposes. Other types, like short_integer, remain incompatible with integer.

[Right Submenu]

 AdaScript versus GCC

 Case Sensitivity

 Reserved Words

 Comments

 Literals

 Bourne Shell Word Expansions

 Fundamental Types

 User-defined Types

 Enumerated Types

 Arrays

 Records

 Basic Assignment

 The @ and % Operands

 Command Argument Shortcuts

 Redirection and Pipelines

 Command Line Interaction

 Built-in Shell Commands

 The Current Directory

 Database Commands

 Flow of Control

 Other Statements/ Subprograms

 External Commands

 Block Statements and Subprograms

 TCP/IP Sockets

 Numeric Formatting with Put

 Interpreter Directives

 Command Line Options

 Command Reference

 ASCII and Latin_1 Character Sets

 Common Error Messages

 Common PHP Functions and the SparForte Equivalent

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