Variables

Variables in Maia may be either external or local, as in C. External variables are declared outside any function, while local variables are declared inside a function. Local variables may be either automatic or static. Automatic variables are initialised whenever their declaration is encountered, while static variables retain their previous value from the last invocation of the function.

A function has no mechanism to access local variables in any other function, and local variables hide external variables of the same name (or, in general, variables of the same name in any outer scope). However, a function can explicitly access a hidden external variable by using the :: global scope operator (foo = ::foo, for example).

Variables have 'block scope', which is essentially identical to C's block scope (a block, denoted by {} parentheses, introduces a new scope).

All variables (apart from the built-in predefined variables) must be declared, with the one exception noted below. Local variables must be declared before their first use, while external variables may be declared at any point in the source code, and potentially after any usage of that variable. The predefined variables are result, and any name that starts with an underscore (_) character.

This program shows some simple examples of variable declaration and usage:

void main() {
   test();           // reports '1 2 3'
   test();           // reports '1 3 4'
   test();           // reports '1 4 5'
}

test() {
   int a;            // local automatic; re-initialised every time this statement is executed
   a++;              // always sets 'a' to 1
   c++;              // increment the current value of 'c'
   static int b = 1; // local static; initialised once, at startup
   report("%d %d %d\n", a, ++b, c);
}

int c = 2;           // external; initialised once, at startup

Note that:

  1. Automatic variables are initialised every time their declaration is executed (on block entry, for example). If the variable does not have an explicit initialiser, then it is is default-initialised (in this case, a is initialised to 0)
  2. Static and external variables are initialised only once, (conceptually) at program startup
  3. Static variables retain their value between function calls
  4. Automatic and static variables must be declared before they can be used (with the single exception noted below); external variables may be declared after any use
  5. Local variables may be declared at any point inside a function; they do not have to be declared at the 'top'

Implicit variables

When the _StrictChecking pragma is set to 0 local automatic variables may be used without first declaring them. In this case, if an assignment is made to a name, and that name has not been declared, it is assumed to be a var. The var is sized to the default word size, and so will normally be a var32 (when implicits are disabled, however, a plain var is actually a var1).

The compiler can carry out some limited error checking on the use of implicit variables (for example, it is an error if an undeclared variable is read before it is written), but the use of implicit variables is generally dangerous in all but the simplest programs. This program uses two implicits, product and i:

#pragma _StrictChecking 0                 // the default value is 1
main() {
   product = 1;
   for(i = 2; i <= 10; i++)
      product *= i;
   report("10! is %d\n", product);        // prints '10! is 3628800'
}

This code will not compile if _StrictChecking is not explicitly set to 0. In this case, the compiler will report that product and i have not been declared.


Initialisation

Both scalar and aggregate variables may be initialised in their declaration. Automatic variables may be initialised with an arbitrary expression (which might include function calls, for example); static and external variables must be initialised with constant expressions.

All variables which are not explicitly initialised are given a default initialisation. 4-state variables are initialised to all-X, while 2-state variables are initialised to 0 (or false, if appropriate).

The format of an initialiser for an aggregate (a structure or an array) is essentially identical to the equivalent C initialiser; elements are read from a brace-enclosed list, and applied recursively to the aggregate. However, error checking is more strictly defined than for C. Conditions which would normally cause a warning in a C compiler (misaligned braces, for example) are flagged as errors in Maia. This code is an example of a simple array initialisation:

void main() {
   int[2,3] a = {{0,1,fn2()}, {3,4,5}};
   assert(
      a[0,0] == 0 && a[0,1] == 1 && a[0,2] == 2 &&
      a[1,0] == 3 && a[1,1] == 4 && a[1,2] == 5);
}

int fn2(void) {
   return 2;
}

Differences from C
  • C does not have a global scope operator (::), so external variables cannot be accessed if they are hidden by a local variable of the same name
  • Automatic variables are not default-initialised in C, and instead have an undefined value
  • External variables must be declared before their first use in C