OADL Program Syntax

At the outer global scope, an OADL programs may contain any or all of the following:

  1. Constant declarations
  2. Variable declarations and initialization
  3. External procedure declarations
  4. Procedure declarations and definition
  5. Class declarations and definition
  6. Object declarations and definition
  7. Public attribute declarations

These elements may be combined in any order. Not all are required; in fact, a procedural entry point named main is not even required. Any of these elements may be enclosed in a namespace declaration for better organization of symbols.

Constant Declarations

A constant declaration associates a name with a constant expression. The syntax for a constant declaration is:

const_decl      : 'const' consts ';'
                ;
consts          : name_init
                | consts ',' name_init
                ;
name_init       : IDENTIFIER '=' expr
                ;

where expr is defined in the Expressions chapter. Most expressions are valid constant initializers; these include scalar constants such as numbers; string constants; and even array and procedure constants. Dynamic values, such as variable references, procedure calls, and object references, are not allowed in constant expressions.

Here are some example constant declarations:

const
    pi = 3.141592653589,
    euler = 2.7182;

const
    three = '3';

const
    name = "Mary Smith";

const
    CardNames = {
        "A", "2", "3", "4", "5", "6", "7", "8",
        "9", "10", "J", "Q", "K"
    };

OADL predefines several constants for convenient use by the programmer. These constants include the floating point Type constants:

Float Half Double

...the integral Type constants:

Int Uint Byte Ubyte Short Ushort
Long Ulong

...the array Type constants:

PackInt PackUint PackLong PackUlong
PackShort PackUshort PackByte PackUbyte
PackChar PackWideChar String WideString
PackFloat PackDouble PackHalf PackBool
List Array

...various other Type constants:

Object Dict Proc Extern Public Pointer
Exception WideChar Char Null Class Type
Bool ArrayType Array[*] Enclosure File

...the predefined exceptions (found in namespace oadl):

TypeCheck RangeCheck ArgCheck AccessCheck AssertCheck
StackCheck ExternCheck ShapeCheck InterruptCheck FormatCheck
UnboundedCheck EndOfFile RegexCheck IoCheck MatchCheck
ProcCheck ExceptCheck NameCheck RedefinedCheck UTF8Check

...system configuration constants (found in namespace oadl):

JMP_SIZE MAXINT MAX_RANK

...various constants for use by OADL input/output routines (found in namespace io):

EOF ErrOut FMT_ADD_BLANK FMT_ADD_PLUS FMT_ADD_TYPE
FMT_ALIGN_ROWS FMT_CENTER FMT_IMMED_ROWS FMT_INTERNAT FMT_LEFT
FMT_NO_BOX_ARR FMT_THOUSANDS FMT_UNSIGNED FMT_UPPER_CASE Input
Output SEEK_CUR SEEK_END SEEK_SET

...and the Null, Bool, and Object constants:

nil true false self

Elements of constant arrays and strings may not be reassigned. For example, the following code with throw a runtime AccessCheck error:

const str = "string";
/* ... */
str[0] = 'f';

Variable Declarations

Variable declarations consist of the var keyword followed by one or more variable names. Variables may optionally have an initializer associated with them. Variables may also optionally have a type specifier associated with them.

An initializer is an expression (see the Expressions chapter). If no initializer is given, the variable is initialized to nil. Note that only procedure-local variables can be initialized to non-constant expressions; all other variables may only be initialized to constant expressions that can be evaluated at compile time.

A type specifier specifies that the given variable may only hold values of a given type. If no type specifier is given, then a variable may hold values of any type. The type specifier consists of a colon followed by an OADL type (Float, Int, Array, PackFloat[3], etc.). If a type specifier is given, assignments to the variable will be type-converted to that type before the value is stored. If type conversion is not possible, a TypeCheck exception will be thrown. See the chapter on OADL Types for more information on type specifiers.

The syntax of a variable declaration is:

var_decl        : 'var' vars ';'
                | 'static' vars ';'
                ;
vars            : var
                | vars ',' var
                ;
var             : qual_name 
                | qual_name ':' type
                | qual_name '=' expr
                | qual_name ':' type '=' expr
                ;

Variables of any scope (except arguments) may be initialized. The syntax is very similar to named constant declarations. For example:

var
    Weight = 82, Height : Int = 183;

Just as with constant declarations, the initializer may be an expression. Procedure-local variables may be initialized by any expression; all other variables must be initialized by constant expressions.

Variables can also be initialized to an unnamed procedure using the same syntax as constants:

var name = proc( args ) { statements };

The value of the unnamed procedure may be determined inside the body of the procedure by using the syntactic convention ( proc ), thus:

var fact = proc(n) { return (n < 2) ? 1 : n*(proc)(n-1); };

This is especially useful given the fact that unnamed lambda procedures do not have access to local variables of the enclosing procedures (see Identifier Scope, below, for more information).

Procedure local variables may be declared using the static keyword. If so, they are placed in the global variable area and retain their values from call to call. Otherwise, they are placed on the stack and have a lifetime only concurrent with the current execution instance of the procedure in which they are declared. For example:

    proc stat() {
        static entered = false;
        "", entered ? "Subsequent call" : "First call", '\n';
        entered = true;
    }

    stat()
First call

    stat()
Subsequent call

Note that, in OADL desk calculator mode, the var keyword may be omitted:

    a = 3
    a
3

Identifier Scope

Constant names, variable names, class names, object names, public names, and procedure names have a scope - a context in which they are valid. There are 5 possible identifier scopes in OADL:

Scope Identifiers allowed
Procedure block local Constants and variables
Procedure arguments Variables
Class Constants, variables, and procedures
Global Constants, variables, procedures, classes, and named objects
Public All public property names

The scopes are given in priority order. Identifiers are first looked for in the nested procedure block scopes, then in the procedure argument list, then in the enclosing class declaration, and finally in the global scope. The public scope is special; names immediately to the right of a . operator are looked for in the public scope, as are names explicitly looked up via the public::name syntax. The names of public and protected variables of class definitions are automatically placed in the public namespace.

Unlake Javascript, nested procedures have no access to names in the enclosing procedure's scope. This allows for a much simpler calling convention. For example, the following procedure will produce a compile-time error:

proc foo()
{
    var a = 1;
    var b = proc() {"a is: ", a, '\n';}
}

External Procedure Declarations

External procedures are system-dependent routines implemented by the OADL runtime or by OADL extension libraries. They are declared in an OADL program via the following syntax:

extern_decl     : 'extern' qual_names ';'
                ;

An external procedure can be defined as the default handler for non-object method calls via the following syntax:

default_public  : 'default' 'public' defpubs ';'
                ;

defpubs         : IDENTIFIER '=' qual_name
                | defpubs ',' IDENTIFIER '=' qual_name
                ;

Default handlers may not be redefined:

    #include "libstd"
    0.->sin()
0.

    default public cos = m ath::sin
Default public cos already defined

See the External Procedures chapter for further discussion.

Procedure Declarations

A procedure declaration either declares or defines a global procedure. The syntax for a procedure declaration is:

proc_decl       : 'proc' qual_name '(' args ')' proc_body
                | 'proc' qual_name '(' ')' proc_body
                | 'proc' STRING qual_name '(' args ')' proc_body
                | 'proc' STRING qual_name '(' ')' proc_body
                | 'proc' qual_names ';'
                ;

args            : /* NOTHING */
                | arg
                | args ',' arg
                ;

arg             : IDENTIFIER
                | IDENTIFIER ':' type
                ;

proc_body       : '{' statements '}'
                | ':' type '{' statements '}'
                ;

where qual_names is the same as in external procedure declarations, above. If no proc_body is provided, the statement merely declares the procedure but does not define the instructions. This is especially useful when linking multiple modules; the actual implementation of the procedure need not be exposed in a header file to be included via an #include statement.

The STRING is a hint to OADL about attributes of the procedure. Currently, the only supported attribute is the indexable attribute, "[]", which tells OADL that the byte code stream of the procedure may be examined by OADL programs. Each byte of the procedure is accessed by the OADL index operator, and the number of bytes in the procedure may be found by the OADL length intrinsic. Indexable procedures may be used in foreach expressions and forall statements. Attempting to access the byte code stream of non-indexable procedures will result in an AccessCheck exception.

For example:

    proc "[]" foo () {return nil;}
    foo.length()
19

    foo[0]
168

The type specifies a type for an argument of the procedure, or return type for the procedure. If no return type specifier is given, then the procedure may return values of any type. See the section on OADL Types for more information on type descriptors. If a type descriptor is used, return values will be type-converted to that type before the procedure returns. If type conversion is not possible, a TypeCheck exception to be thrown.

The statements production may be found in the Procedures chapter.

The following are examples of valid procedure declarations:

proc a, b, c;
proc fact(n : Int)
{
    if (n > 1) {
        return n * fact(n-1);
    }
    else {
        return 1;
    }
}

Class Declarations

A class declaration either declares or defines a class. A class is an abstract data type that includes data storage along with operations on the data. The syntax for a class declaration is:

class_decl      : 'class' qual_name '{' props '}'
                | 'class' qual_name '(' names ')' '{' props '}'
                | 'class' qual_names ';'
                ;
props           : /* NOTHING */
                | props prop
                ;
prop            : 'public' oneprop
                | 'protected' oneprop
                | 'operator' oper_decl
                | oneprop
                ;
oneprop         : var_decl
                | const_decl
                | proc_decl
                ;
oper_decl       : operator no_name_proc
                ;
operator        : '|'      | '^'     | '&'     | '<'         | '>'
                | '+'      | '-'     | '*'     | '/'         | '%'
                | '~'      | '!'     | '##'    | '=='        | '!='
                | '<='     | '>='    | '<<'    | '>>'        | '@'
                | '=>'     | '**'    | '++'    | '--'        | '#='
                | '\|'     | '\^'    | '\&'    | '\<'        | '\>'
                | '\+'     | '\-'    | '\*'    | '\/'        | '\%'
                | '\=='    | '\#='   | '\!='   | '\<='       | '\>='
                | '\<<'    | '\>>'   | '\=>'   | '\~='       | '\**'
                | '!-'     | '{' '}' | '[' ']' | '[' '=' ']' | '(' ')'
                | '#[' ']' | '#[' '=' ']'
                ;
no_name_proc    : '(' args ')' '{' statements '}'
                | '(' ')' '{' statements '}'
                ;

The args and statements productions may be found in the Procedures chapter. Further discussion of the semantics of classes and objects may be found in the Classes and Objects chapters.

The following are examples of valid class declarations:

class a, b, c;
class foo {
    public proc bar() {say("bar");}
}
class foobar(foo) {
    public proc bletch {say("bletch");}
}
class complex {
    protected var real, imag;
    public proc create(r, i) { real = r; imag = i; }
    operator + (rhs) {
        var res;
        if (rhs ?= complex) {
            res = new complex(real + rhs.real, imag + rhs.imag);
        }
        else {
            res = new complex(real + rhs, imag);
        }
        return res;
    }
    /*...*/
}

Object Declarations

An object declaration declares or defines an object. An object is an instance of a class. The syntax of an object declaration is:

/* Forward declarations and object definitions with no property
 * assignments
 */
obj_decl_list   : obj_decl_noprop
                | obj_decl ',' obj_decl_noprop
                ;
obj_decl_noprop : qual_name qual_name
                | qual_name qual_name '(' exprs ')'
                ;

/* Object definitions with property assignments */
obj_decl_props  : qual_name qual_name obj_proplist
                | qual_name qual_name '(' exprs ')' obj_proplist
                | '...' qual_name obj_proplist
                ;
obj_proplist    : '{' obj_props '}'
                | '{' obj_props '...' '}'
                ;
obj_props       : /* NOTHING */
                | obj_props prop_init
                ;
prop_init       : IDENTIFIER '=' expr
                ;
exprs           : /* NOTHING */
                | exprlist
                ;
exprlist        : expr
                | exprlist T_COMMA expr
                ;

Further discussion of the semantics of classes and objects may be found in the Classes and Objects chapters.

An example object declaration is:

box mybox {
    length = 10
    width = 5
    height = 4
}

Public Attribute Declarations

When creating an OADL module to be linked in by other programs, it is very useful to be able to declare classes and their public interfaces in a header file without specifying their full implementation. The public attribute declaration syntax declares an interface:

public_decl     : 'public' names ';'
                ;

An example using public attribute definitions is:

class box;
public length, width, height, draw, resize, setcolor;

The box class is (presumably) defined in an OADL source file which is to be separately compiled and linked in with the main OADL program.

Namespaces

Global identifiers may be grouped into namespaces for better management of identifier conflicts. By default, there are four namespaces that OADL defines: the anonymous global namespace, the public namespace, the io namespace, and the oadl namespace. The public namespace contains all names defined via public statements. All other global names are placed by default in the anonymous global namespace, unless a new named namespace is being constructed. This is similar to Java and C++ namespaces in syntax and concept. An OADL program may define a new namespace via the namespace declaration:

namespace_decl  : 'namespace' name '{' items '}'

qual_name       : name
                | name '::' name
                | '::' name
                ;

qual_names      : qual_name
                | qual_names ',' qual_name
                ;

The items in a namespace may refer to each other without qualification. Items outside the name space may only refer to items inside the namespace via the :: syntax or with the using statement:

using_stmt : 'using' one_using ';'
           | using_statmt ',' one_using ';'
           ;
one_using  : 'namespace' name
           | 'extern' string
           | name '::' name
           ;

If the program includes the using namespace statement, all of the symbols in the namespace may be used without qualification. Additionally, the program may selectively include only certain symbols via the using ns :: name syntax:

using statements may be placed at the global scope or the procedure block scope, but not the procedure argument scope or class scope. If a using statement is placed at the procedure block scope, the included names are only accessible until the end of the enclosing block.

Here is an example of a using statement that imports a single symbol into the global namespace:

#include "libstd.oah"

using math::cos;

The public namespace is implicitly used, and OADL will disambiguate names from the public namespace and the current namespace scope based on context - names immediately to the right of the . operator are assumed to be from the public namespace. To prevent this disambiguation, place parenthesis around the name to the right of the . operator:

var create = public::destroy;
obj.(create)(); // actually calls the "destroy" method

Names from the anonymous global namespace may be accessed via the ::name qualified name format:

var n = 1;
proc main()
{
    var n = 2;
    // Should print "n = 2; ::n = 1"
    "n = ", n, "; ::n = ", ::n, '\n';
}

The using extern syntax is used to interface an OADL program with an external procedure library; see the chapter on External Procedures for more information.

Back to Lexical Elements

Continue to OADL Types

Return to Introduction