Chapter 6. Command Language

Chapter 6. Command Language

Command Language Overview

The scripting language in Aten is based syntactically on C/C++, with echoes of useful Fortran features included for good measure. This means that if you're familiar with C/C++ then writing a script, command, or filter for Aten should be relatively straightforward. There are lots of C primers on the web, so the following sections provide only a brief summary of the style of the language.

General Input Style

Keywords, variables, and function names are case sensitive, so 'for' is different from 'For' (all internal commands in Aten are in lowercase). Individual commands must be separated by a semicolon ';', and newlines are optional. For example:

int i; printf("Hello there.\n"); i = 5;

...and...

int i;
printf("Hello there.\n");
i = 5;

...are equivalent. Whitespace characters (spaces and tabs) are ignored and may be present in any amount in any position in the code.

Individual lines or parts of them may be commented out. The presence of either the hash symbol '#' or '//' in a line means 'ignore rest of line'. Note that the '/*...*/' commenting style is not supported.

Variables

There are several standard rules for variables within Aten:

  • They must be declared before use

  • They are strongly-typed, i.e. they hold a specific type of value (e.g. an integer, a character string etc.)

  • They must begin with an alpha character ('a' to 'z'), but may otherwise consist of letters, numbers, and the underscore ('_')

  • They may not be named the same as any existing function or internal keyword

Since variables are strongly-typed, trying to set an integer variable from a string will not work since these are incompatible types. However, standard C-style string conversion functions are available - see String Commands.

So, to initialise some variables, assign them values, and print them out, we would do the following:

int i;
double result;

i = 10;

result = i*5.0;

printf("i multiplied by 5.0 = %f\n", result);

In addition to the standard 'int' and 'double' types, a 'string' variable exists for the storage of arbitrary-length character strings, and do not need to have a length specified on their creation (they will adjust dynamically to suit the assigned contents). Literal character strings should be surrounded by double-quotes. A set of variable types exist that are able to contain references (not copies) of various objects within Aten, e.g. atoms, models, unit cells, etc. Variables of these types are declared in exactly the same way as normal variables (see Variable Types for a list of available types). A 'vector' type is provided for convenience and stores a triplet of real ('double') values. There is no boolean type - use an 'int' instead - but the built-in constants 'TRUE' and 'FALSE' may be used in assignment, etc., and correspond to the integer values '1' and '0' respectively.

All variables will default to sensible values (e.g. empty string, numbers equal to zero) on initialisation. To create a variable with a specific value simply do the following:

int i=1,j=2,k=1001;
double myvar=0.0, angle = 90.0;

Arrays

Arrays of most variable types are allowed (some, for instance the aten type, don't really make sense as an array). Arrays are requested by providing a constant size (or an expression) in square brackets after the name:

int values[100], i = 4;
double q[i];

Here, two arrays are created - an array of 100 integer numbers called 'values', and an array of four floating point numbers called 'q'. Array indices always run from 1 to the size of the array, unlike C in which arrays run from 0 to N-1, and Fortran in which it is possible to specify custom lower and upper indices for an array.

Arrays can be initialised to a simple value on creation, setting all elements to the same value...

string s[4] = "Nothing";

...or each element to a different value using alist enclosed in curly brackets:

string s[4] = { "Nothing", "Nicht", "Nada", "Not a sausage" };

Also, all array elements can be set to the same value with a simple assignment:

int t[100];
t = 40;
[Note]See Also:

Predefined Constants

Several predefined constants exist, and may not be overridden by variables of the same name. All predefined in constants are defined using uppercase letters, so the lower case equivalents of the names may be used as variables, functions etc.

Table 6.1. Built-in Constants

NameTypeValue
FALSEint0
NULLint0
PIdouble3.14159265358979323846
TRUEint1

In addition, all element symbols found in the input will be seen as their equivalent integer atomic number. So, instead of having to provide short strings containing the element name to, for example, the transmute command, simply the capitalised element name itself may be used. Thus...

transmute("Xe");
transmute(Xe);
transmute(54);

...are all entirely equivalent.

Blocks, Scope, and Variable Hiding

As with C, variable scope is employed in Aten meaning that a variable may be local to certain parts of the code / filter / script. In addition, two or more variables of the same name (but possibly different types) may peacefully co-exist in different scopes within the same code / filter / script. Consider this example:

int n = 4, i = 99;
printf("%i\n", i);
if (n == 4)
{
	int i = 0;
	printf("%i\n", i);
}
printf("%i\n", i);

...will generate the following output:

99
0
99

Why? Well, even though two variables called i have legitimately been declared, the second declaration is made within a different block, enclosed within a pair of curly braces. Each time a new block is opened it has access to all of the variables visible within the preceeding scope, but these existing variable names may be re-used within the new block in new declarations, and does *not* affect the original variable. Hence, in the example given above, after the termination of the block the final printf statement again sees the original variable i, complete with its initial value of '99' intact. Note that if a variable is re-declared within a scoping block, there is no way to access the original variable until the scoping block has been closed. Blocks can be nested to any depth.

Functions

For functions that take arguments, the argument list should be provided as a comma-separated list in parentheses after the function name. For instance:

newatom("C", 1.1, 0, 4.2);

Arguments may be constant values (as in this example), variables, arithmetic expressions, or any other function that returns the correct type. For functions that (optionally) do not take arguments, the empty parentheses may be omitted. A list of all functions, their arguments, and their return types is given in the command reference.

All functions return a value, even if it is 'no data' (the equivalent of 'void' in C/C++). For instance, in the example above the newatom command actually returns a reference to the atom it has just created, and this may be stored for later use:

atom a;
a = newatom("C", 1.0, 1.0, 1.0);

However, if the return value of any function is not required then it may simply be 'forgotten about', as in the example prior to the one above.

User Defined Functions

User-defined functions can be defined and used in Aten, taking a list of variable arguments with (optional) default values. The syntax for defining a function is as follows:

type name ( arguments ) { commands }

type is one of the standard variable types and indicates the expected return value type for the function. If no return value is required (i.e. it is a subroutine rather than a function) then type should be replaced by the keyword 'void':

void name ( arguments ) { commands }

Once defined, functions are called in the same way as are built-in functions. The name of the function obeys the same conventions that apply to variable names, e.g. must begin with a letter, cannot be the name of an existing keyword, function, or variable. The arguments are specified in a similar manner to variable declarations. A comma-separated list of types and names should be provided, e.g.:

void testroutine ( int i, int j, double factor ) { ... }

Our new subroutine 'testroutine' is defined to take three arguments; two integers, i and j, and a double factor. All three must be supplied when calling the function, e.g.

printf("Calling testroutine...\n");
int num = 2;
double d = 10.0;
testroutine(num, 4, d);
printf("Done.\n");

When defining the function/subroutine arguments, default values may be indicated, and permit the function to be called in the absence of one or more arguments. For instance, lets say that for our 'testroutine', the final argument factor is likely to be 10.0 on most occasions. We may then define a default value for this argument, such that if the function is called without it, this default value will be assumed:

void testroutine ( int i, int j, double factor = 10.0 ) { ... }

printf("Calling testroutine...\n");
int num = 2;
testroutine(num, 4);
testroutine(num, 4, 20.0);
printf("Done.\n");

Both methods of calling 'testroutine' in this example are valid.

Return Values

For functions defined to return a specific type, at some point in the bpdy of the function a suitable value should be returned. This is achieved with the return keyword. Consider this simple example which checks the sign of a numeric argument, returning '1' for a positive number and '-1' for a negative number:

int checksign ( double num )
{
	if (num < 0) return -1;
	else return 1;
}

If an attempt is made to return a value whose type does not match the type of the function, an error will be raised. Note that, once a return statement is reached, the function is exited immediately. For functions that do not return values (i.e. those declared with 'void') then return simply exits from the function - no return value need be supplied.

Arithmetic Expressions and Operators

Arithmetic operates in the same way as in C, and utilises the same operator precedence etc. Similarly, comparison operators (less than, equal to etc.) are the same as those found in C.