PMzone logo

Book cover

Project Guide

Reference Manual

Most programming languages are accompanied by some combination of Reference Manual, User Guide, and Programming Language Rationale.

The Reference Manual with Rationale is the subject of this project. It represents a formal document describing the syntactic and semantic constructs of the language so users can understand the fundamental rationale and justification for why certain aspects of the language were selected.

Typical reference manuals are written by the authors or teams during language development and are primarily used by software design engineers. Programmers generally refer to them only in case of some language ambiguity. The standard described in the accompanying project guide is evolving as the following concepts are constructed and tested:

Why Another Programming Language

Why would someone spend the time and energy creating yet another programming language? There are a number of very good reasons. Among them are the following:

Language Development

The development of complex, commercially viable software applications generally involves large project teams and seemingly unlimited resources. Since most casual programmers are constrained by limited resources, the Adagé project is intentionally scaled to what I feel one individual can practically undertake in a reasonable amount of time.

Not withstanding the availability of resources, certain criteria still exist for the development of well-designed programming languages. In this project I present key design considerations driving Adagé development. These factors are divided into three key areas: readability, writability, and reliability.


Ada Reference Manual

Project 2: Learn How to Write Specifications for a New Programming Language

This project presents an overview on how to establish requirements for a new programming language and then develop software specifications to meet those requirements. Specifications outline the standard form, meaning, and intent of a programming language. Their purpose is to promote the uniformity of a program language in a given problem domain. The content in the accompanying project guide is taken from the Adagé Reference Manual, Volume 1, First Edition.

NOTE The Adagé Reference Manual and accompanying project guides are currently being written and tested. They are not yet available for release.


Before we can go about the business of writing a compiler for a new programming language, we need to name, identify, and document the primary specifications that will guide our design effort. As already noted, our new language is called ADAGÉ, which formally stands for ADAptable and Generally Extensible programming language. I refer to it simply as Adagé, which when translated loosely means axiomatic lexicon or standardized language. In either case, Adagé is pronounced "add–ĕ–shay".

Adagé is an adaptable programming language. It's rich set of core lexical and syntactical constructs are adapted from a number of imperative programming languages including Ada 2012, C, Modula-3, Pascal, Python, and SPARK 2014. A considerable amount of work went into selecting and testing the best features found in these languages and incorporating them into Adagé. Thus, Adagé implements a relatively small set of highly standardized constructs aimed at facilitating mastery of the language and enforcing correctly constructed programs.

Adagé is an extendable programming language. It achieves extensibility through modularization, low-level assembly language, and external support libraries. Instead of avoiding machine-level facilities, Adagé actually embraces them. This bi-level capability provides programmers with unprecedented control over hardware not possible using high-level language structures alone.

Finally, the accent over the letter "é" denotes the language's international focus and imminent conversion from UTF-8 format to UTF-16. This transition is just a small part of the overall effort to make Adagé programs more reliable. Reliability demands complete mastery of a programming language's fundamental syntax so that programmers can remain focused on writing correct code rather than struggling with writing code correctly.

CAUTION Be aware that no computer programming language can be all things to all programmers and Adagé is no exception. Adagé does not attempt to incorporate every possible existing language feature. Keeping true to its design goals, Adagé is implemented as a procedurally imperative language for general purpose programming where variable length string processing, extended precision arithmetic operations, machine-level hardware access, and lexical consistency remain cornerstones.

Page Top

Rationale for Adagé

My purpose for creating Adagé is to overcome inherent anomalies associated with predecessor languages, namely: ambiguous semantic definitions, excessive complexity, redundant expressive power, unsafe features, and lack of built-in machine-level access. With these shortcomings in mind, Adagé's primary design goals focus on readability, writability, and reliability. Let's look at each design goal one at a time.


Readability is a measure of how easy a program is to read and understand—sometimes weeks after the original source code was written. Although a number of factors go into designing a readable language, it turns out that program syntax is a major source of confusion and compiler complexity. Key factors contributing to this confusion include ambiguous identifier names, inconsistent syntax and semantic constructs, and machine dependency leading to the overuse of different kinds of control structures and data types.

To increase readability, the syntax of a programming language should be made as simple as possible without resorting to overly terse style as seen in the C/C++ programming languages. Additionally, the language should contain sufficient constructs together with just enough expressibility to compose realistic solutions to problems in the chosen programming domain.

As programs increase in size and complexity, readability becomes more significant. Many software designers point out that it is more important for a language to be readable than it is to be easy to write. For this reason, Adagé places more emphasis on making programs easy to read. As a consequence, Adagé programs tend to be slightly more verbose; requiring a little more typing than similar programs written in BASIC or C. However, the results are well worth the extra work.

Adagé readability design goals:

Simplified Lexical Elements

Adagé programs are written as a sequence of lines of text containing the following characters:

A line of Adagé code can be thought of as a sequence of characters known as lexical elements. Here is an example line of code:

procedure Add_Two_Numbers (x, y, z : integer; f : float => 123.45)  ~ Comment

This example consists of eleven different kinds of lexical elements called tokens. These tokens include:

Reserved Words and Keywords

Reserved words are special names with special meaning . For example, the name procedure is a reserved word and cannot be used as a user-defined name or else an exception will be raised by the compiler.

Keywords, on the other hand, are words that have special meaning only in special contexts. For instance, put is a keyword because it is not a reserved word. Most keywords like put are found in external libraries and cannot be used as user-defined identifiers.

Special names in Adagé are used to make programs more readable by providing names for actions to be performed. Also, they are used to separate the syntactic parts of statements and programs.


An identifier is a term used to give tokens a name. A name is just a string of characters that identify reserved words, keywords, variables, subprograms, commands, types, subtypes, and packages.


IDENTIFIER ::= (letter) [underscore {letter | digit}] [letter | digit]
      letter ::= A-Z | a-z
      digit  ::= 0-9
  underscore ::= _
declare     ~ Reserved word
My_Total    ~ User-defined identifier
IS          ~ Reserved word
put         ~ External library keyword

 1. Identifiers are used to name program entities
 2. Names must contain only letters (A-Z | a-z), digits (0-9), and underscores (_)
 3. Names must begin with an upper-case or lower-case letter (A-Z | a-z)
 4. Names must fit onto a single line where all characters are significant
 5. Names cannot begin with a digit (0-9)
 6. Names cannot begin or end with an underscore (_)
 7. Names cannot contain two consecutive underscores (__)
 8. Names cannot contain spaces (" ")
 9. Names cannot contain symbols (!, @, #, $, %, &, +, *, etc.)
10. Names are not case sensitive (declare is the same as DECLARE and Declare)
11. Special words include reserved words and keywords (function, IS, put, etc.)
12. Special words cannot be used for user-defined names

Identifiers must begin with a letter (A-Z | a-z) followed by an optional string of letters, digits, and underscores. The termonology in the previous format definition uses brackets [ ] to signify that zero or more of the symbols contained within the brackets can be used. Braces { } indicate that one or more characters contained within must be used while parentheses ( ) indicate that one of each character contained within must be used in the order listed. The ::= symbol means is defined as. The vertial bar | represents an OR operator.


Colons are used to separate expressions into their component parts. The colon symbol is used in expressions to mean "is of type". That is, the left side of a colon lists one or more objects, the colon itself means TYPE, and the right side of the colon defines the object type.


COLON ::= identifier : type

procedure (x: integer; y: float)
function Compute_Total_Cost (price : float) return float
total_cost : float

 1. Colons ":" separate identifiers and their types
 3. Colons may be preceded by one or more blank spaces
 2. Colons MUST BE followed by at least one blank space

Note that colons are used to separate identifiers from their types. Think of the colon as a reserved word that tells the compiler the list of variables has ended and their type follows. After the type has been declared, an optional value may be assigned (x : integer = 15).


Many casual programmers regard semicolons as a nuisance and a source of hard-to-detect bugs. In fact, the way BASIC and FORTRAN use the compound symbol CR-LF to mark the end of a line of code seems much more intuitive. Adagé forgoes the manditory use of semicolons to end expressions and statements.

In older programming languages, semicolons are used almost exclusively for the convenience of the compiler. In my humble opinion, well-built and well-thought out compilers generally do not require semicolons as terminators. However, programmers used to languages that use semicolons can feel free to use them. Adagé simply treats them as statement terminators and not separators as do some languages like Pascal. Think of semicolons as reserved words that tell the compiler one statement has ended and another is beginning.


SEMICOLON ::= statement[; statement][;]

procedure Output_Stings (str1 : string; str2 : string) ~ Semicolon separates two statements
put (str1); new_line   ~ Semicolon separates statements
put (str2); new_line;  ~ Semicolon optionally terminates a line of code
end Output_Strings;    ~ Semicolon optionally terminates a block of code

 1. Semicolons ";" separate sequences of statements
 2. Semicolons may optionally terminate a statement
 3. Semicolons may optionally terminate blocks of code
 4. Semicolons MUST BE followed by at least one blank space or EOL mark

Notice in the previous examples how semicolons allow multiple statements to be placed on the same line. In general, this format is discouraged as it has the potential to reduce readability and introduce hard-to-detect bugs. However, without the semi-colon, only one statement per line is allowed. Also, the semicolons at the end of new_line and Output_Strings are not needed nor encouraged.


Commas are used to separate a sequence of variables and arguments in expressions in the same way semicolons separate statements. Expressions are enclosed in parentheses and contain lists of one or more variables or arguments. They are typically found in subprograms. Commas also are used to separate package names and aspect expressions.


COMMA ::= (parameter[, parameter]) | package-name[, package-name]

procedure Output_Numbers (a, b, c: integer)  ~ Commas separate parameter names
include TextIO.lib, IntegerIO.lib            ~ Commas separate package names
put (a, b, c)                                ~ Commas separate argument names

 1. Commas "," separate sequences of variable-names, argument-names, and package-names
 2. Commas MUST BE followed by at least one blank space

Notice that commas are used to separate arguments in the put(a, b, c) statement instead of using the ampersand & symbol, which represents catenation. The reason for this has to do with subprogram overloading. The put procedure must convert each integer argument to a string in preparation for output to the stdout device. Overloading is discussed shortly. Think of the comma as a reserved word that tells the compiler that one expression or the name of one identifier has ended and another one follows.


All Adagé subprograms contain parentheses ( ) to enclose formal parameters when they are declared. This increases readability by distinguishing subprograms from commands. For instance, PUT("Some text!") includes opening and closing parentheses to designate put as a procedure and not a simple command. It also allows the elements within the parentheses to spill over onto multiple lines. Parentheses ( ) are also used in expressions to enforce operator precedence and to contain subprogram parameter lists.


PARENTHESES ::= (expression[; expression]) | 
                (identifier operator identifier)[ operator identifier]

procedure Write_Numbers (my_integer: integer;
                         my_float: float;
                         my_string: string)     ~ Parens allow multi-line statement list
function Add_Numbers (x, y: float) return float ~ Parens enclose subprogram parameters
value = (x + y) * z                             ~ Parens enforce operator precedence
put("Programming is fun.")                      ~ Parens enclose subprogram arguments

 1. Parentheses "( )" enclose formal parameters
 2. Parentheses enforce operator precedence
 3. Left-parentheses "(" MUST BE preceded by at least one blank space
    except that left-parentheses may directly follow a subprogram name
 4. right-parentheses ")" MUST BE followed by at least one blank space

Think of parentheses as reserved words that tell the compiler that a set of one or more expressions are enclosed or an operator has priority.


Brackets [ ] are used to enclose index values when declaring array bounds. Since strings represent one-dimensional arrays, brackets are used to also indicate string bounds.


BRACKETS ::= [array_dimension[, array_dimension]]

prompt: string [12] = "Hello World!"       ~ Declare 12-character string
My_Buffer: string [32766]                  ~ Declare 32766-character string buffer
type My_Type is array [5] of integer       ~ Define array type containing 6 integers
type Big_Array is array [10, 20] of float  ~ Multi-dimensional array
str[5] = "a"                               ~ 'a' is assigned to the fifth element of str

 1. Brackets "[ ]" enclose array indices
 2. Left brackets "[" MUST BE preceded by at least one blank space
    except that left-brackets may directly follow an array name
 3. Right brackets "]" MUST BE followed by at least one blank space

Think of brackets as reserved words that tell the compiler a set of array indices are enclosed. The indices represent the size of declared arrays.


Braces { } are used to enclose literal values when declaring enumerated types.


BRACES ::= {literal[, literal]}

type Paint_Color is {green, red, silver, white, yellow}            ~ Paint_Color literals
type Day is {Mon, Tue, Wed, Thr, Fri, Sat, Sun}                    ~ Day literals
type Aircraft is {helicopter, single-engine, multi-engine, jet}    ~ Aircraft literals
type horses is enum {Arabian, mustang, quarterhorse, thoroughbred} ~ Optional enum reserved word

 1. Braces "{ }" enclose enumerated literals
 2. Left braces "{" MUST BE preceded by by at least one blank space
 3. Right braces "}" MUST BE followed by at least one blank space

Braces substitute for the reserved word enum, which is optional. Ada style languages do not use the enum reserved word to indicate enumerated sets, but instead employ parentheses ( ) and rely on context to define any enclosed identifiers as enumerated literals. Adagé uses braces { } to indicate enumeration literals.


Various languages use different representations to designate comments. Many languages also permit multi-line block comments. With one exception, there isn't much utility in using multi-line block comments. They are extremely difficult to implement in a compiler and have the potential of causing hard-to-detect bugs during program development. In modern programming languages, good coding practices discourage the implementation and use of block comments that do not mark each individual line as a comment.

Adagé employs the tilde ~ symbol to designate single-line comments. Comments continue to, and are terminated by, the end of line. This means everything on the same line following a tilde symbol to the end of line is ignored by the compiler. Comments are considered empty space.

Selection of the tilde symbol comes from the fact that Adagé does not allow operator overloading beyond those operators found in the standard library package. I considered Ada's double-hyphen --, but decided that the tilde was more appropriate in reinforcing the no overloading rule. I got in the habit of using the tilde even though it doesn't look as elegant as the double-hyphen. The tilde also implements easier in the compiler.


COMMENT ::= ~ [non_end_of_line_characters]

~ -----------------------------------------------------------------
~ Standalone comment on a separate line
~ Must start every new comment line with a tilde symbol
~ -----------------------------------------------------------------
x = y + z              ~ This is a comment at the end of a line

 1. Comments on every line must begin with a tilde ~ symbol
 2. Everything following a tilde to the EOL mark is ignored by the compiler
 3. Multi-line blcok comments ARE NOT allowed in Adagé

Programming Note on Punctuation Symbols

Punctuation marks include symbols such as: braces, brackets, colon, comma, parentheses, quotations, range mark, semicolon, and double-hyphen. All other symbols comprise arithmetic and logic operators. Normally, Adagé does not allow symbols to be part of an identifier's name as shown in this example: Add_Numbers(X, Y: integer).

Here, the comma and colon are postfixed to the names of parameters. The right parentheses appears postfixed to the keyword integer. The left parentheses may be considered prefixed to the beginning of the x parameter or postfixed to the end of the subprogram name. Many programmers do not realize that placing a left-parenthese right after an identifier name like in this example: Add_Numbers(z: integer) is very confusing to a compiler. The compiler thinks that as far as it is concerned, "Add_Numbers(z:" appears to be one long name that has violate the rule that says a symbol cannot be part of an identifier name and so raises an exception.

It would seem these prefixes and postfixes violate the Adagé rule about not using symbols as part of a name. However, these punctuation symbols are allowed to be used in similar fashion as their natural language counterparts. The Adagé compiler has been designed to be smart enough to determine when symbol characters merely represent symbols or have mistakenly been added to a name. It does this by understanding the context in which the symbols are used. Because the compiler can make these distinctions, punctuation symbols may be used just as their natural language counterparts might be implemented. Look at the following examples.


braces        {red}             { red }
brackets      str[10]           str [ 10 ]
parentheses   put(x)            put ( x )
colons        z: float          z : float
commas        a, b, c           a , b , c
semicolons    put(x); new_line  put(x) ; new_line
tilde         ~Comment          ~ Comment

There are some adherents in the programming community who reason that symbols should never be placed adjacent to identifier names. They are emphatic that there should always be at least one space separating them. For example: put ( x ) or put (x) has been suggested in order to avoid compiler confusion and ambiguity. In any case, Adagé requires a blank space before and after operators such as *, -, +, /, and =. A space before and after the compound delimiters =>, .., ==, !=, >=, >=, and <>. This spacing is implemented to enhance readability and reduce ambiguity.

Block Structures

Adagé distinguishes between declarations, which introduce new identifiers, and statements that manipulate them. The language requires that new identifiers be declared (defined) before they are used. In order to make programs more readable, declarations and statements are placed in different sections of a program so they are easy to locate and identify. Main program block structures begin with the reserved word declare, which contains the declarative section. The declaration section is followed by the reserved word begin denoting the start of the statement section. The statement section contains program statements and ends with the end reserved word. Subprograms contain context-clauses, program-headings, and allow the reserved word declare to be optional.


  ... declarations
  ... statements
end subprogram-name

include IntegerIO                     ~ Context clause
procedure Add_Numbers (x, y: integer) ~ Program heading
    z : integer                       ~ Declare section
begin                                 ~ Statement section
    z = x + y
    return z
end Add_Numbers

Context Clause

A context clause informs the compiler that one or more named packages are being incorporated by the program. The compiler includes the relavent code from each package needed to carry out specified operations in the program. The name of one or more packages are listed after the reserved word include.


CONTEXT-CLAUSE ::= include package-name [, package-name]

include TextIO
include IntegerIO, FloatIO

Context clauses can only appear at the very beginning of a program header.

Program Heading

Program headings mark the beginning of a program. Programs can name one of three different subprogram-types: procedure, function, and method.


subprogram-type subprogram-name [(parameter-list)] <is>

procedure Add_Two_Numbers (x, y: integer) is
function Divide_Two_Numbers (m, n: float) return float

  1. Program headings tell the compiler the name of a program and where it begins.
  2. Not sure if the reserved word is will be used in final production

Methods are used to declare classes and define their associated methods. More discussion on classes and methods as the language evolves.

Declare Section

A block of code starts with the reserved word declare and contains object declarations, type and subtype definitions, and optional comments. The purpose of the declaration section is to declare named objects and to give names to the various user-defined objects and definitions that will be used in the program.

Objects represent an abstraction of computer memory locations that hold values. Adagé has two kinds of objects: variables and constants, and two kinds of definitions: type and subtype. These names are called identifiers.

Variables and constants are elaborated during program execution. Elaboration simply refers to the process of setting asside physical storage space in memory for each declared object and then placing a value for that object into its assigned memory address. A program can change the value of variables, but constants retain their assigned values throughout the life of the program.


DECLARATION ::= type-declaration | subtype-declaration | variable-declaration
  TYPE_DECLARATION     ::= type type-name is base-type constraint-definition
  SUBTYPE_DECLARATION  ::= subtype subtype-name is base-type range range-constraint
  VARIABLE_DECLARATION ::= variable-name : [global | static | constant] base_type [= value]
  type Days is {Mon, Tue, Wed, Thr, Fri, Sat, Sun} ~ Type declaration
  subtype Pos_Int is integer range 1 .. 100        ~ Subtype declaration
  Top_Score : Pos_Int = 100                        ~ Initialized variable declaration
  x : integer                                      ~ Uninitialized variable declaration
  pi : constant float = 3.1415926536               ~ Constant declaration

 1. Variables and constants MUST BE declared before they can be used
 2. Variables MUST BE assigned a value before they can be used
 3. Variables ARE NOT assigned default values
 4. Constants MUST BE assigned static expressions when declared

Adagé provides four optional storage classes for variables: local, global, static, and constant. Subprogram variables without a declared storage class are local in scope by default. Variables declared outside subprograms are global and accessible to any subprogram. Static variables are local variables that maintain their last assigned value and remain persistent throughout the life of the program. Constants represent objects that must have assigned values that can be evaluated at compile time. They can be either global or local depending on where they are declared and cannot be changed during the life of a program.

Adagé also allows variables to be initialized (assigned a value) when declared. Variables that do not have assigned values before being used will cause the compiler to raise an error message. Unlike the C programming language, Adagé does not assign a default value of zero to uninitialized variables. The reason for this is that in safety-critical programs, a default value could result in unexpected, and sometimes deadly, outcomes. Adagé takes the position that it is safer to force a correct value to be assigned than to simply insert a default value.

Statement Section

The statement section contains the sequence of actions to be performed. In some imperative programming languages, blocks of code are sometimes bracketed by symbols braces { } as used by C/C++ or BEGIN-END constructs used by languages like Ada, Modula, Pascal, and SPARK. Adagé uses the BEGIN-END form to mark blocks of code because this method provides clearer and more consistent syntax as opposed to using braces. This makes it easier for programmers to read and understand the code. It also makes it easier for the compiler to understand the intent of the programmer and to ensure objects have appropriate visibility.


STATEMENT ::= begin {identifier (expression)} end subprogram-name

    ~ Sequence of statements
    Top_Score = Top_Score - Number_Of_Wrong_Answers
    Circle_Area = Radius * pi
    put (Circle_Area)
end Calculate_Area

Notice how the statement section starts with the reserved word begin and ends with the reserved word end. This assists programmers in readily identifying the beginning and ending of a block of statements. Unlike languages such as C/C++, which employ braces, Adagé programmers do not need to include an end-of-code block comment after the closing brace. Semicolons after the reserved word end also are unnecessary.

Visibility and Scope

Programming languages such as Ada, BASIC, C/C++, Modula, and Pascal have extremely complicated scope, visibility, and overloading rules that have been shown to cause considerable confusion. In Adagé, scope and visibility are made much simpler. Variables declared in one subprogram are local to that subprogram. This means that variables declared local in one subprogram are only visible (have scope) in that subprogram. This allows variables with the same name to be reused in other subprograms.

Fewer Basic Data Types

Some languages such as C/C++ have several data types based on the underlying machine architecture. Types such as char, short, int, long int, long long int, unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, float, double, and long double. This is absurd. Certainly programs need a character type, an integer type, and a floating-point type, but very few programs ever need more than a couple variations of these basic types.

Since Adagé is designed for the 64-bit environment, it implements the largest data types and forgoes the smaller ones as we'll see shortly. However, Adagé doesn't totally abandon every possible data size and type. These can always be conjured up using Adagé's inline assembly language capability.

Types and Constraints

Every object can be described by its type. The type of object determines the range of values that can be assigned, amount of memory storage space needed to store the type object, and set of operations that are defined for values of the specified type. For instance, Adagé integers are whole numbers in the range -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807, take up 64-bits of memory space, and can be operated on by a set of arithmetic operators that includes addition, subtraction, multiplication, division, and modulus.

Types are declared using a syntax that is slightly different from the syntax of object declarations. This difference emphasizes the fact that declaring a type is different from declaring an object, which has a value and takes up physical memory space. Type declarations consist of the reserved word type, an identifier to be associated with the type, and the reserved word is followed by the base-type (parent type) and constraint-definition.

Every type has a name, which is introduced in the type declaration, and every type declaration introduces a new type completely distinct and incompatible with any other type.


TYPE_DECLARATION ::= type identifier is base-type constraint-definition
    where type-definition ::= range_constraint | index_constraint | literal_list
type circle_area is float range 0.0 .. 99.99     -- Float type with a range_constraint
type color is {beige, gray, tan, white, yellow}  -- BRACES contain enumeration literals
type tint is {blue, gray, white, yellow}         -- Enum type with a literal_list
type Boolean is enum {false, true}               -- Enum type using optional reserved word enum
type minutes is integer range 1 .. 60            -- Integer type with a range_constraint
type Final_Results is array [0 .. 9] of integer  -- Array type with an index_constraint

 1. Every type must have a name
 2. Type declarations introduce new types
 3. New types are incompatible with other types
 4. Unconstrained types are not allowed
 5. Constraint bounds must be statically verifiable

Type declarations specify that the identifier following the reserved word type is the name of the type whose representation is defined by the base-type and constraint-definition. The form of the constraint-definition following the reserved word is differs for each kind of type. The constraint-definition typically specifies one of the following: range of values, index bounds placed in brackets, or set of enumerated literals placed in braces.

Notice in the previous examples that the float and integer type declarations include range constraints. The array type declaration contains an index constraint while the enumeration type declarations contain lists of literal identifiers. The compiler enforces defined constraints during compilation and execution. Once defined, constraints are static and cannot be changed by the program. An exception is raised when a value is assigned to an object that is outside its defined bounds.

Name Equivalence

Adagé uses name equivalence as opposed to structural equivalence to identify types. This means that separately declared types are incompatable with one another. This feature allows the compiler to detect logic errors at compile time.


type List_1 is integer range 1 .. 100
type List_2 is integer range 1 .. 100
A : List_1 = 15    -- Instantiation of List_1
B : List_2 = A     -- Illegal action; A and B are not compatible types

The fact that the two types shown in the previous example have the same range of integer values (structural equivalence) does not make them compatible. In order to be compatible, name equivalence requires that the two types be derived of types having the same name, not the same structure. Therefore, variable A, which is of type List_1, cannot be assigned as a value to B, which is of type List_2.

Subtypes and Constraints

Subtypes are created from existing types. Subtypes are denoted by the reserved word subtype and contain a set of values that represent some subset of the values from its base (parent) type. All Adagé subtypes must define a set of range-constraints. Range-constraints can consist of a subset of the existing type set or they can contain the complete set of the existing type set. It is important to note that subtypes do not define new types. They are merely subtypes of an existing type and so are compatible with the base type and all child subtypes of the base type.


SUBTYPE_DECLARATION ::= subtype identifier is base-type range range-constraint
    where range-constraint ::= set_of_values | set_of_indices | set_of_literals

subtype day_of_month is integer range 1 .. 31      -- Constrained value_range
subtype Auto_Color is color range gray .. yellow   -- Constrained enumeration_range
subtype Room_Color is tint range blue .. white     -- Constrained enumeration_range
subtype Weekend_Days is Days range Sat .. Sun      -- Constrained enumeration_range 
subtype Test_Scores is Final_Results range 1 .. 6  -- Constrained index_range

 1. Every subtype must have a name and set of constraints
 2. Subtype declarations do not introduce new types
 3. Subtype variables cannot contain uninitialized values
 4. Initial values MUST BE constant expressions

The subtype definition specifies that the identifier following the reserved word subtype is the name of the subtype. The range-constraint following the reserved word range defines the constraints associated with the subtype.

Note that two incompatable subtypes, Auto_Color and Room_Color, have similar corresponding literal values, namely gray and white. In this situation, the compiler generally does not know whether for instance the color gray is of subtype Auto_Color or Room_Color. The basic issue is that the ordering of "gray" for instance, is different in each subtype declaration. In Auto_Color, "gray" is in the first position while in Room_Color, "gray" is in the second position. The different positional relationships give each enumerated literal a different ordinal value. The solution is that Adagé requires fully qualified dot notation to make proper literal name assignments.

Adagé does not allow unqualified type expressions. This eliminates the possibility of any ambiguity. Fully qualified dot notation must be used when enumerated literals are assigned.


type-name.subtype-name -- Fully qualified dot notation

Auto_Color.gray   -- The gray in subtype Auto_Color
Room_Color.gray   -- The gray in subtype Room_Color

 1. Unqualified expressions are not allowed
 2. Assigned enumerated literals MUST BE fully qualified

In some languages such as Ada, the unqualified form can be used in programs by adding the use reserved word in the declaration section of a subprogram. However, in the case where a subprogram uses two types containing literal values with similar names, the fully qualified format still must be used to represent specific type values. Adagé does not implement the use reserve word. This requires that all Adagé enumeration type values must implement the fully qualified dot notation format. This substantially reduces ambiguity and hard-to-detect bugs.

ADDITIONAL TEXT ON SCALAR TYPES. Scalar Types In SPARK, the range in a range constraint shall be static. Furthermore, no static range shall be a null range, i.e. the upper bound of a static range shall be greater than or equal to the lower bound of the range.

Basic Data Types

Adagé contains the usual predefined and user-defined data types. Predefined data types are declared in the standard library package and consist of ordinal, real, and text data types. Ordinal types include: integer, natural, modular, Boolean, enum, and char. Real types include: float, decimal, and complex. Text types include only strings. Note that char types are predefined enumeration types with 256 values. The Boolean type is also a predefined enumeration type with two values. Strings are predefined constrained array types with a size limited only by the amount of available memory.

User-defined data types consist of composite, structure, and pointer types. Composite types include: array, record, union, and file. Structure types include: queue, stack, tree, dict, set, list, and ring. Pointer types include ref and variant types. For now, Adagé does not implement pointer types as will be explained later. Structure types are designed and implemented in the Structured Program Library project.

Ordinal Types


Integer Types


Integer, modular, enumeration Ada 87, Boolean Ada 90, character Ada 132

Natural Types



Modular Types



Enumeration Types


In the case of enumeration types, the compiler assigns ordinal values to each member starting with 0. This means that the order of each enumeration literal is significant. For instance, in the example of the TYPE color, beige is assigned a positional value of zero, gray is assigned 1, and so forth. This allows Boolean operations to be performed on enumeration types such as: if gray < yellow then.

Boolean Type



Character Type



Real Numeric Types


float, decial, complex, Ada 81

Floating Point Types


Integer, modular, enumeration Ada 87, Boolean Ada 90, character Ada 132

Decimal Types



Complex Types



Composite Data Types


1984 73, Modula 9

arrays Ada 117, Strings Ada 133, Records Ada 143, Sets?, Lists?, Vectors?

Array Types


Integer, modular, enumeration Ada 87, Boolean Ada 90, character Ada 132

Record Types



Union Types



File Types



Structured Data Types


The Structure library Ada2012 Container Library 699

queue, stack, tree, dictionary, set, list, ring



Integer, modular, enumeration Ada 87, Boolean Ada 90, character Ada 132



















Pointer Types


pointers, variants Ada 189

Reference Types


Integer, modular, enumeration Ada 87, Boolean Ada 90, character Ada 132

Variant Types



Subprogram Types


Procedure Ada2012 171, Function Ada1996 276; Ada2012 161, Method, Thread

Objects (OOP)



Distinct Subprogram Structures


Subprograms are the principal construct for implementing abstraction in most programming languages. A subprogram performs operations when called by a routine, which waits for the subprogram to finish before continuing execution. Most routines pass data in the form of arguments to be worked on to called subprograms. Most languages require subprograms to be declared before they can be used. Declarations assist compilers in conducting static and dynamic checks on parameters and returned values.

[Ada 161] Adagé implements four clearly distinction subprogram types: (1) procedures, which do not return values to a calling routine, (2) functions, which return manditory values to calling routines, (3) methods, which define operations on objects in object-oriented programming, and (4) threads, which provide concurrent programming capabilities.

It should be noted that although only functions return values, all Adagé subprograms contain exception codes that can be used in programs to capture errors when they occur.



[Ada 171] Adagé procedures are called as statements by calling routines. Adagé functions are called as components of expressions and return values as part of the expression. Adagé procedures and functions take the following forms:



Ada 161

Note that a function subprogram must return a value to the calling routine.







Expressions and Control Structures


A programming language with a large number of basic control structures is more difficult to learn than one with a smaller set of built-in control structures. Typically, programmers only use a small portion of any language's syntax and this set may differ from programmer to programmer. Adagé focuses on a manageable set of orthogonal constructs.

Orthogonality is a concept where a small set of language components can be combined in a narrowly defined number of ways to create the control and data structures of a language. The goal of orthogonal languages is to reduce redundancy, minimize complexity, and eliminate ambiguity to the extent possible. Key principles of orthogonality are symmetry and consistency.

Adagé focuses on making its syntax consistent. Examples include rules that reserved words cannot be used as user-defined identifiers, identifiers cannot contain any symbols other than underlines, symbols have only one meaning, subprograms cannot be overloaded, and aliasing is not allowed.

Operators (reduced set--no overloading)


Ada 169

Adagé implements the following operators:

Arithmetic operators use separate, consistant, context correct symbology and are not inappropriately overloaded like they are in many other languages. All these orthogonal mechanisms are designed to help avoid confusion, improve readability, and enhance compiler development and performance.

For instance, the division "/" and multiplication "*" symbols are not used in combination to mean something else like comments (i.e., //, /*, */). The multiplication sign "*" and modulus sign "%" are not used as variable prefixes to indicate pointer and dereference operations (i.e., *myvar or %myvar). Division and multiplication symbols are strictly used, strangely enough, for division and multiplication operations. Even the plus "+" sign is reserved for arithmetic operations. The ampersand "&" is used for string concatenation and not the plus sign "+".



Ada 68, 97

Control Structures (reduced set)


Ada 101, 1984/45



IWBasic electronic user's manual.



Ada 149, 1984/17






Modula 28, Ada 71,862

Page Top



Writability is a measure of how easy a language can be used to write source code for specific problem domains.

Writability is concerned with how easy it is for a programmer to think though and create a program for a specific problem domain. Writability directly relates to three key areas: (1) amount of learning needed to actually write a program in a particular language, (2) level of effort required to write the program, and (3) how many lines of code it takes to actually make the program work correctly.


Adagé writability design goals:



Expressiveness relates to how easy it is to read and write code that is understandable for both the compiler and the programmer. In other words, the expressive power of a language conveys the breadth of ideas that can be represented and communicated in that language. In terms of Adagé, expressive power translates into intuitively readable constructs that are concise and standardized across all implementations.

Adagé is designed to be written once and read many times. This is the heart of the vision for Adagé and so extra work is put into writing code in order to make it clearly readable and understandable over time.

Clear Syntax Structure


Reduced Number of Constructs



Pedagogical considerations also suggest to us that drastic simplifications must be made. Safetycritical work demands complete mastery of a programming language, so that the programmer can concentrate on what he or she wants to say rather than struggle with the means of expression. In this regard,

SPARK is presented here as a complicated set of restrictions of a very large language, to allow direct comparison with full Ada; however, a much lighter description of SPARK could be produced, which would make the language as easy to learn as Modula-2. Initial training based on a SPARK manual, bringing out the essential ideas of high-integrity programming in Ada, might be very worthwhile.

For instance, here's an example of a typical Adagé procedure:

procedure Add_Two_Numbers (z, x: integer)
  include Integer_IO
  var z: integer = 0 -- Declare a local variable
  z = x + y          -- Assign the sum of x and y to z
  put_integer (z)    -- Output the value of z to stdout
  new_line (1)       -- Print a CR/LF
end procedure

This example demonstrates the expressiveness of Adagé. By making code expressive, it remains easy to understand even after months have gone by and you are relooking the code. However, expressive, self-explanatory code comes at the expense of code that may seem verbose at first. Adagé is not terse like languages such as C or assembly. It is designed to be easy to read and understand time after time.

Syntax Complexity. Complex syntax is a major source of programmer confusion and hard-to-find errors. Making programming languages more readable involves syntaxes that include indentation, lots of white space, understandable identifier names, use of small words in lieu of symbols, straightforward data and control structures, unambiguous implementation of operators,

consistent standardization and utilization of program flow patterns, elimination of curly braces and semicolons, restriction to single-line comments, and block structuring that requires subroutine code sections to be enclosed within BEGIN and END keywords. The following example demonstrates some of Adagé's simple syntax concepts:

function Add_Two_Integers (x, y: integer) return integer -- White space before open parenthesis
  var z: integer = 0  -- Space between VAR reserved word and z, colon placed next to z
begin                 -- Start of a block of statements
  z = (x + Y)         -- Space before and after = and + marks
  return z            -- Space between reserved word RETURN and variable z
end function          -- Code block closeout

Notice that punctuation marks and white space resemble what you would find in any natural language such as English. Colons are placed adjacent to the last word in the independent clause and white space appears after commas and before open parentheses marks. White space also appears before and after symbols such as equal signs, arithmetic symbols, comparison operators, and comment marks.

Additionally, indentation is used to offset enclosed blocks of code. Although Adagé does not strictly enforce natural language formatting style, these rules greatly enhance readability.

Machine-Level Dependencies


Machine Dependency. Machine dependency manifiests itself in a number of ways that effect readability. One machine-related dependency is expressed in the number of basic data types and the way they are declared by a particular language. For instance, the INTEGER type can be expressed as either a signed or unsigned integer.

Further, the number of integer types can encompass a wide range of sizes such as SHORT INT (8-bit signed), USHORT INT (8-bit unsigned), INT (16-bit signed), UINT (16-bit unsigned), LONG INT (32-bit signed), LONG LONG INT (64-bit signed), INT64 (64-bit signed), and UINT64 (64-bit unsigned). The plethora of data types originates from the underlying storage space requirements of the microprocessor architecture.

Adagé's high-level language constructs eliminate machine dependency to the extent possible. The result is that programmers are insulated from the underlying system architecture. For example, Adagé defines one INTEGER data type, which is implemented as a 64-bit signed data element. Where machine-dependent data types are needed, programmers can define and implement them using inline assembly language.

Data Abstraction


Data types consist of a set of values and a set of operations that can be applied to those values. An abstract data type represents data that can be completely characterized by the way in which the values of the data type are related to each other by the operations that can be performed on them. Data abstraction is a means of representing data that looks like something in the real world rather than a random set of numbers.

TALK TO BOTH DATA AND SUBPROGRAM ABSTRACTION. p.14 Concepts of Programming Languages

An abstract data type consists of a set of values plus a set of operations on those values. Adagé employs two data abstraction components. The first includes the concept of identifying the operations that can be used to manipulate particular kinds of data. The second involves hiding the memory storage requirements for data types from the programmer.

When using a high-level language, the programmer should not have to be concerned with the underlying engineering design. For instance, Adagé offers several basic data types that include: integer (signed whole numbers), natural (unsigned whole numbers), float (floating point numbers), fixed (decimal fixed-point type), pointer, enumeration, array, record, and variant.

These Adagé types use 8 bytes of storage space. Other basic types include: character (predefined enumeration type), string (predefined array type), and Boolean (predefined enumeration type). Except for strings and arrays, all Adagé parameters are passed to subprograms by value unless otherwise specified by the programmer.

Having said that, there are two constructs that allow programmers to create and use 1-byte, 2-byte, 4-byte, 8-byte, 10-byte, and 16-byte types. The first method is to utilize inline assembly language code. Inline assembly code blocks allow all NASM defined data types (i.e. byte, word, dword, qword, oword) to be defined and used in Adagé programs.

Adagé also permits various built-in data types to be subtyped and constrained. This is the beauty of Adagé's extensibility. For instance, the range of Adagé's natural data type is 0 to 18,446,744,073,709,551,615 (a.k.a. C's unsigned long long type, BASIC's UINT64 type, assembly's QWORD). The NATURAL data type uses 64-bits (8 bytes) of memory space for each natural value.

A second construct that allows programmers to specify specific memory sizes for stored objects is provided by size specifiers. Size specifiers allow programmers to define 1-byte, 2-byte, 4-byte, 8-byte, 10-byte, and 16-byte memory storage allocation. Size specifiers are found in packages contained in the system library. They are implemented for the convenience of advanced programmers who may be writing system-type programs including hardware device drivers and don't want to resort to inline assembly code.

Note that subtypes do not define new types. Constraint ranges are elaborated using the TO reserved word to separate the low range from the high range value.

Encapsulation and Information Hiding


Encapsulation and information hiding are closely related to data abstraction. Encapsulation is the concentration of a group of dependent subroutines into one section of code. In Adagé, this grouping is called a package much like in Ada. Encapsulation facilitates separation of related code without having to be concerned about the details of how the underlying data types and operations work.

Encapsulation is the use of programming language constructs to restrict the ways that information provided in one part of a program can be used in other parts. It is a formal means of hiding information such that data definitions, elaboration, and operations on the data are concentrated in one place in a program structure. Adagé uses the reserved word private in package declarations to define data types and operations that need to be isolated from users. A private type can be declared only in the specification part of a package declaration.

A private declaration has the form:

The private declaration is defined in the visible part of the package. This declaration must be accompanied by a full type declaration for the same type in the private part of the package.

Example package containing encapsulated private components:

The previous example shows four encapsulated components: three parameters and one procedure. The three parameters, area, length, and width are declared as private and therefore, are hidden from users in terms of their implementations. When variables are declared as private, they are considered to be a part of the package body.

Users cannot see the definition of variables or details of how the procedure Calculate_Area works because the functionality and definition of these components are included in the package body. The only visible parts of the package are the interfaces shown in the specification part of the package.

By declaring the types length and width as private, we restrict the allowable operations to assignment and comparison for equality or inequality. The user of the package Acreage knows nothing about the internal structure or representation of these types. The declarations following the private clause are conceptually part of the package body and are therefore hidden from the user. This prevents users from being able to modify the meaning of the area, length, and width data types and the operations on them.



Need Text. p.138 Comparing and Assessing Prog Languages.

The purpose for implementing generics is to increase readability. This is accomplished by Adagé because it can determine which subprogram to use based on the type declaration of the variable or form of the literal data being passed. The distinction of each subprogram allows the compiler to optimize type checking, reduce errors, and minimize overall program size.

Another key point is that these subprograms do not add to the number of reserved words since each subprogram is included in separate I/O packages and added to a program only when explicitly included.

Generic units facilitate writing general-purpose, reusable software components. Generic units are templates from which several similar subprograms and packages can be produced without having to rewrite duplicate code over and over. There are two kinds of generic templates, generic subprograms and generic packages. Generic subprograms include both generic procedures and generic functions.


The concept of generic units is an interesting one, which does find significant applications in the Ada Input-Output library. However, it is another feature which seriously complicates the formal definition of Ada. Also, the code re-usability which it aims to provide is not achieved as easily as one might imagine: it is still necessary to prove correctness of every instantiation of a generic object.

The proofs may be simplified by first establishing some properties of the generic object in abstract terms (assuming for instance that the operators which it employs obey certain axioms), and then showing that each instantiation is a valid concrete interpretation. But if the generic unit is nontrivial, the required proofs may remain non-trivial also. Furthermore, generics cause overloading, which we are anxious to avoid. We do not believe that, in our application area, the complexity introduced by generic units is justified.

Generic units are just templates. They represent general-purpose, reusable component structures that make it possible to use templates to solve problems using different data types without the redundancy of writing a function for each type. A generic declaration defines a generic unit, which can be either a generic subprogram or a generic package.

For instance, we can declare a generic subprogram to add two numbers together like this:

This function accepts two arguments "x" and "y". Being generic means that the arguments passed to the function can be any discrete or real type. As a templated generic function, you write the function once, but use it multiple times using different types (i.e. polymorphism) because the code associated with the generic subprogram is generalized to the different data types, which the compiler recognizes.

Since a declared generic unit is only a template; it cannot be used as defined. In order to use the generic unit, it must be instantiated first. Instantiation creates a corresponding type.

Example instantiations of the generic subprogram Add_Numbers:

Now we can call the subprogram Add_Integers (100, 50), which will use the generic unit to return an integer value of 150. Alternatively, we can call the subprogram Add_Floats (15.25, 25.75), which will return a float value of 41.0 also using the algorithm contained in the generic unit. Notice how we only had to write one subroutine to perform operations on various types.



Extensibility of a programming language is concerned with how well the language allows users to add new features. BASIC allows the addition of keywords. C and Ada allow user-defined types. C and Ada also provide component libraries to extend the power and capability of the programming language.

Extensibility relates to the mechanisms a language employs that allow users to add new features to a program. Adagé provides for separately compiled modules, user-defined types, subprograms, classes, and add-in libraries and library components.

Example Adagé program extensions:

Page Top



Reliability is a measure of how well a program performs to its stated design specifications under all operating conditions.

Reliability includes the mechanisms employed by a programming language to make it trustworthy, dependable, and correct. Some programmers replace the term "reliable" with "correct".

Core constructs are made more reliable by imposing a number of restrictions on their number and use, ensuring each construct can be statically and dynamically determinable, incorporating a system of annotations and specifications that define and quantify identifiers, and guaranteeing funtions do not have side effects by passing function variables as parameters.

Reliability concerns the quality, trustworthiness, dependability, and correctness of a programming language. Three aspects must be taken into account when designing a reliable language. First, reliability is costly and not every language requires the same level of reliability. Reliability turns out to be more of an economic issue rather than a technical issue.

I decided that Adagé must strive for correctness and therefore, accept the price that must be paid in terms of additional validity checks and program size that accompany reliable programming languages. My sense is that a programming language should not crash or otherwise cause undue harm to a user during execution.

Second, reliability may significantly hinder program development by placing strict requirements on the syntax. Reliable languages require substantially more validation checks and strict typing. Additionally, built-in reliability features typically increase program size and reduce overall speed.

Third, reliability is associated with a language's grammatical syntax. The less complex the syntax, the easier the language is to learn and parse. Complex syntax adds to a programmer's learning curve and amount of time spent writing a program. Complex syntax also takes it toll on the underlying compiler as additional static checking is performed and additional code is included in a program to perform dynamic checking.

There are many ways to measure syntax complexity, but the best method is to simply count the number of tokens needed to create various program structures. By this measure, languages that require more tokens are acknowledged to be more complex.

class="magic"Adagé Reliability design goals:

Strict Type Checking


A strongly-typed programming language is one in which each type of data is predefined as part of the programming language. That is, all constants and variables defined for a given program must be described with one of the intrinsic or user-defined data types.

Adagé is a strongly typed programming language where every object belongs to exactly one type. Adagé requires all constants and variables to be explicitly declared before they are used. Adagé type checking is performed statically at compile time. No automatic type conversion. The programmer must explicitly convert one compatable type to another.

Coercion is allowed in two cases: (1) integer and natural numeric types may be coerced to floating point types and (2) subranges may be coerced to their parent types. Strong typing also means that the type of a variable or constant cannot be changed after it is declared.

Strong typing has been deliberately chosen to ensure programs act in a safe, consistent manner and that no unexpected results occur at runtime due to type mismatching. For this reason, Adagé programs may compile to slightly larger executables than do other languages.

Example type checking in Adagé:

As shown in the examples, strong data typing imposes a rigorous set of rules on the user and thus guarantees a certain consistency of results. The disadvantage is that it limits the creativity of the user in terms of inventing data types not anticipated by the compiler.

Static Type Checking

Dynamic Type Checking


Adagé also performs dynamic type checking during runtime in order to perform type casting and type checking of variables used in FOR loops without having to explicitly declare them. Unions use the variant type, which are dynamically checked when instantiated during runtime.

Dynamic type checking can be turned on or off using the stong_type (on|off) pragma.

Bounds Checking


Additionally, Adagé performs bounds checks on types and arrays and does not allow aliasing or pointer arithmetic. These features are enforced in order to prevent memory leakage, stack overflows, and other security-related issues from occurring.

Exception Handling


Exceptions are run time hardware or software errors that require special processing. Exception handling is the process programming languages use to catch run time anomalies and handle them in a manner that prevents the program from suddenly crashing unexpectedly.


Exceptions, designed for dealing with errors or other exceptional situations, might at first sight seem very desirable for safety-critical programming.

However, it is easier and more satisfactory to write a program which is exception-free, and prove it to be so, than to prove that the corrective action performed by exception-handlers would be appropriate under all possible circumstances4; if checks with recovery actions are required in a program, these can be introduced without resorting to exception handlers, and in general the embedded code will be easier to verify. Since exceptionhandling also seriously complicates the formal definition of even the sequential part of Ada we believe it should be omitted.

Exception handling is the process of responding to runtime errors or other program anomalies that normally terminate program execution. Errors can be either hardware-based (i.e. out-of-memory) or software-based (i.e. division by zero). In either case, what often results is a program fatal error crash. Adagé is different. It employs exception handlers to keep programs from terminating prematurely during execution by allowing them to gracefully recover from error situations.

When an error (exception) is raised, Adagé immediately looks at the end of the current block of code for the reserved word exception. If it is found, along with the appropriate definition, then program control passes to the exception handler for execution. In this way, Adagé programs can attempt to recover from an error condition or in the case that no safe method exists for continuing the program, Adagé informs the user and then terminates in an orderly fashion.

The process of announcing that an error has occurred is called raising an exception. Responding to the announcement if called handling the exception. This process is calling throwing and catching using such keywords as TRY-ENDTRY, CATCH-ENDCATCH, or THROW-ENDTHROW.

Example Adagé exception handler:

Following the exception, Adagé will attempt to return to the calling routine, and normal sequence of instructions if possible. In situations where a loop may be in process, it would be impossible to return to that particular block of instruction. In this case, Adagé requires the programmer to define what happens when a fatal error occurs as part of the exception handler.

Restricted Anonymous Types



Restricted Aliasing



Restricted Operator Overloading



Restricted Subprogram Overloading


Subprogram Overloading. Subprogram overloading occurs when a procedure or function uses the same identifier name to perform operations on different data types. For instance, BASIC overloads the PRINT statement. PRINT is used to output integer and real number values, characters, strings, and Boolean types.

C implements the PRINTF function to accomplish similar operations on various data types. It is left to the compiler in these languages to identify the correct subprogram version to use. In accordance with the general philosophy of uniqueness of names espoused by languages such as SPARK, Adagé subprogram names are not allowed to be overloaded.

Verification Specifications





Programming by contract constructs are used to provide extra support for developing high integrity programming languages. Contract-aware programs include additional information regarding data flow, state and proof, interface definitions, and pre-execution correctness. Contract-type information is added by using aspect specifications to discover program anomalies by improving the detail description of an interface and ensuring that the code implements the interface correctly.

Adagé provides built-in programming by contract constructs to increase the amount of information concerning program interfaces. These static and dynamic verification mechanisms flush out the details associated with subprogram interfaces and are used to uncover potential problems associated with understanding subprogram interfaces and to identify potentially incorrect interface implementations.

Adagé implements two forms of contract constructs: (1) annotations and (2) aspect specifications. These contracts are encoded in the core of the language and are designed to reduce ambiguity by providing information about the application designer's intentions and requirements for certain program components.

Consider the following Adagé subprogram code:

What does this procedure actually do? Without comments, a user really has no idea about the purpose of the procedure. At first glance, it appears that it might increment the variable "counter" by one, but what is the purpose of the "x" variable? Does "x" represent some kind of increment value and if so, does it have a default value? On the other hand, is "x" optional and do nothing at all if not defined?

With Adagé, contract specifications are added to the code to provide additional information regarding what the procedure actually does and to ensure that variables are actually passed and are within any specified ranges.

Here is an example Adagé procedure containing aspect specifications:

The aspect specification information tells us that Increment_Counter will use the global variable named "counter" previously defined in the same package as Increment_Counter and that the value of "counter" is dependent on the values of "counter" and "x" that are passed to the procedure. The aspect specification also limits the value of "x" to the range 1 .. 10. These contract conditions are statically checked during compilation and dynamically checked during execution to help reduce both ambiguity and potential for any subprogram interface errors.

Pre- and Post-Conditions

Use of Predicates such as RANGE, etc.

System-Level Programming


Languages such as Modula-3 and Pascal provide users with a high-level syntax where restrictions are strongly enforced. However, this approach makes it difficult for users to do system-level programming. Adagé overcomes this weakness by making inline assembly language programming a standardized part of the language.

Adagé provides the necessary features to allow programmers to get close to the hardware. The key mechanisms for this are the inclusion of external object libraries and the use of the assembly and end assembly keywords. These block structure keywords allow NASM assembly language instructions to be embedded within Adagé subprograms and passed unaltered directly to the NASM assembler.

Example inline assembly code:

One note about Adagé is in order. Adagé is not designed to be portable per se (i.e. mobile devices, non-Windows OS, etc.), embedded in hardware devices, used to build hardware drivers, or to develop video games. Its primary design is to crunch lots of very large numbers extremely fast and accurately and handle string operations such as sorting and searching efficiently and effectively in near real-time.

It is also designed with safety in mind, which means it performs rigorous checks on data types and array bounds while limiting coercion and aliasing. It standardizes symbology and enforces syntax rules. Alternate forms of syntax use generally are not allowed.

Page Top

© 1997-2017 Transtar Management Services, Inc. All rights reserved. Terms of Use