Previous Next Table of Contents

## 3. The GENESIS Script Language Interpreter

The GENESIS Script Language Interpreter (SLI) provides a framework in which a simulation programmer can define and manipulate GENESIS elements. The SLI interprets statements in the GENESIS simulation language, and constitutes the operating system ``shell'' at work when you are running GENESIS.

The simulator shell is an interpretive operating environment which lets you:

- execute GENESIS routines (e.g., help, step, stop, openfile)
- execute operating system shell commands (e.g., ls, cat, vi)
- define variables (integer, floating point, or string types)
- evaluate arithmetic expressions
- invoke control structures such as conditionals and loops (if...then, while, for, or foreach loops)
- define and execute your own functions

You can perform these operations in either of two ways:

- enter commands directly at the keyboard (the ``command line'')
- read commands from text files (``scripts'')

User-defined SLI scripts are used to glue the pieces of a simulation together. XODUS graphical objects that you use to define the front end of a simulation and GENESIS data handlers are all controlled from SLI scripts.

The shell contains a library of basic commands which you can augment through the addition of compiled C commands or interpreted SLI functions.

## 3.1 GENESIS Statements and Commands

GENESIS routines are called by entering a routine name followed by any necessary arguments and options separated by whitespace.

      routine-name arg1 arg2 ... -option1 option1-arg1 ... -option2 ...

A valid routine-name is a script identifier consisting of any combination of alphabetic (A-Z or a-z) or numeric (0-9) characters, or underscores (``_'') -- all other symbols are invalid characters in an identifier.

Routine arguments consist of literal strings of characters, results of other GENESIS routines or expressions, strings of characters including whitespace enclosed in double quotes and the value of parameters passed to a script or function. Combinations of these which appear together without any intervening whitespace are concatenated and passed as a single argument to the routine.

Routine options follow command arguments, and are introduced by a dash character. Option names may be abbreviated to any unambiguous length. Each option may accept additional arguments following the option name.

A carriage return marks the end of a statement, so you do not need to use semicolons at the end of a line. However, you can use semicolons to terminate statements as a way to include multiple statements on a single line. Several examples:

      genesis > reset; step 100

      genesis > int a, b, c ;  a=1 ;  b=2 ;  c=3
      genesis > if( a < b); echo {a} is less than {b}; end
      1 is less than 2

In other cases, the ``command'' returns a value and might more properly be called a function. In the documentation for GENESIS commands, we will use the term ``routine'' for built-in GENESIS commands, and will reserve the term ``function'' for a script function that is written in the GENESIS scripting language. The procedure for creating your own functions is described under Functions.

The following two examples illustrate how a routine call can be supplied as the argument to another routine or as a value in a script language expression:

      genesis > echo { getfield /neutral_test x }
      genesis > float length = { getfield /cell/soma len }

Routines used in the context of an argument to another routine or within a script language expression must be enclosed in curly brackets.

All GENESIS statements are subject to these and other basic syntactical rules. If a statement you submit violates these rules, the interpreter will respond with an error message (and ignore your instruction).

## 3.2 Creating Variables

In GENESIS, you create a variable by entering a variable ``type'' specifier at the command line, followed by the variable name (a script identifier), optionally followed by a value to assign to the variable:

        variable-type variable-name [= expression]

You can create any of three types of variables at the GENESIS shell:

Type | Meaning | Example Value
int | integer number | 5, 18, 6000, -34
float | double precision floating-point number | 5.0, 3.14, -2001.4
str | character string | five, 5.0, hello

Here are some examples of correct variable declarations:

      genesis > int a
      genesis > float PI = 3.141593
      genesis > float myfloat = 2*PI
      genesis > float floatstr = "6.3"
      genesis > str hi = "hello there"

Note that case is significant for variable names (as in other areas): ``PI'' is different from ``Pi'', which is different from ``pi''. Also note that the character sequence ``6.3'' could be the value of either a floating point variable or a string variable.

Once you have defined a variable, you can change its value using an assignment statement:

      variable-name = expression

For example:

      genesis > a = 40
      genesis > myfloat = a + 2*PI
      genesis > hi = "The value of myfloat = " @ myfloat

Note that GENESIS does not have array variables. However, it has several objects that contain internal data structures that may be used as arrays. Further details are given in the documentation for Tables and Arrays.

### Local and Global Variables

Variables declared in a function are local to the function. Those declared outside of a function in a script (or script that is included with the ``include'' statement) are global variables. When a value is assigned to a global variable, it will affect any statements or functions that follow this assignment. One often refers to ``constants'' in a GENESIS script. These are not true constants, but are just variables thatare not expected to take on new values during the course of a simulation.

Good object-oriented programming style discourages the use of global variables. When possible, it is best to localize variables within functions, or to use oject fields to store values. However, it is often useful to use global variables in order to make them easily accessible throughout the simulation scripts. Scripts that create prototype channels (see Scripts/neurokit/prototypes) usually define global variables for ionic reversal potentials, and the cell reader ( readcell ) makes use of the global variables RM, CM, RA and EREST_ACT.

GENESIS provides the following routines for accessing global script variables.

Routine | Description
addglobal | Declares a global variable; variable name may be a variable.
getglobal | Returns global variable value; variable name may be a variable.
listglobals | Lists currently defined global variables and functions.
setglobal | Sets value of global variable; variable name may be a variable.

### Using Variables (The echo Command)

Script variable values are retrieved by using the variable name in the context of an expression. Expressions are formed using variable names and operators in the usual way. (See Operators.) In some contexts, the value of a variable or an expression must be evaluated by by enclosing it in curly brackets in order to distinguish between the string of characters and the value represented by the character string. This is particularly true when the expression is to be evaluated as an argument of a GENESIS routine or script language function. (See Functions.)

The echo routine is useful for seeing how an expression will be evaluated:

      genesis > float pi = 3.14159
      genesis > float y

      genesis > y = pi/4
      genesis > echo y
      y
      genesis > echo {y}
      0.7853975
      genesis > echo pi/4
      pi/4
      genesis > echo {pi/4}
      0.7853975
      genesis > echo {sin y}    // WRONG!
      0
      genesis > echo {sin {y}}  // CORRECT
      0.7071063519

As with many languages, GENESIS will cast expressions involving both floating point and integer variables to floating point. It will also convert a string to a float if the string is a valid representation of a number. For example,

      genesis > int i = 5
      genesis > int j = 2
      genesis > float x = 2
      genesis > str num = "5"
      genesis > echo {i/j}
      2
      genesis > echo {i/x}
      2.5
      genesis > echo {num/2}
      2.5
      genesis > num = "i/x"
      genesis > echo {num}
      i/x
      genesis > echo {{num}/2}
      ** Error - CastToFloat: Error casting 'i/x', using 0.0
      0

Often it is useful to use a string variable name to hold the name of a global variable. For example, one may want to pass the name of a global variable to a function that declares, sets, or retrieves the value of the variable. However, normal GENESIS syntax for declarations and assignments does not permit a variable a name to be specified by a string variable. The last three of the following GENESIS statements are illegal, and will produce error messages:

      str name = "foo"
      float x

      float {name}  // want "float foo"
      {name} = 5.5  // want "foo = 5.5"
      x = {{name}}   // want "x = foo", or equivalently, "x = {foo}"

The correct way to accomplish these results is to use the routines for accessing global script variables with the name of the global variable held in a string variable.

      addglobal float {name}
      setglobal {name} 5.5
      x = {getglobal {name}}

## 3.3 Functions

As with any programing language, GENESIS lets you define functions in order to make your script programs more modular and easier to modify. You should group function definitions together at the beginning of a script, preceding any statements that will use them. The listglobals routine will list any user-defined functions.

The general form of a function definition in GENESIS is:

            function function-name [ (arg1 [, arg ... ] ) ]
              statement-1
                .
                .
                .
              statement-n
            end

For example:

            function print_area(length, diameter)
                float length, diameter
                float area
                float PI=3.14159
                area = PI*diameter*length
                echo The area is {area}
            end

            function link_compartment(channel,compartment)
                addmsg {channel} {compartment} RAXIAL Ra previous_state
                addmsg {compartment} {channel} AXIAL previous_state
            end

The SLI also lets you define functions interactively at the GENESIS prompt. For example:

            genesis > function my_echo(inputval)
            ? echo { inputval }
            ? end
            genesis > listglobals
            function        my_echo
            genesis > my_echo 33
            33

The return keyword allows a return from the function before the end statement. It may optionally be used to return a value. For example:

            function make_positive(num)  // a silly version of "abs"
                float num
                if (num >= 0)
                    return {num}
                else
                    return {abs {num}}
                end
            end

You are not allowed to define a function within another function. Normally, you cannot refer to a function in another function before it is defined. For example, the following script will produce an error message:

            function func2
                func1 "This is a test."
            end

            function func1(string)
                str string
                echo "The value of the string is: " {string}
            end

            func2

However, adding the line

            extern func1

at the beginning of the script will give the desired result:

            The value of the string is:  This is a test.

The keywords function, return, end, and extern are features of the interpreter, not normal GENESIS commands; consequently, they do not appear in a listing of GENESIS routines (e.g., as given by the listcommands routine).

The routines argc and argv are often used inside of function definitions to return the number of arguments and the array of arguments which are passed to the function.

## 3.4 Operators

The table below lists the basic operators available in GENESIS, from highest to lowest precedence. These operators can be applied to variables or constants within a GENESIS expression.

Operators | Function
- | unary minus
`**` | power
`*` / % ~ | multiply, divide, modulo (int only), bitwise complement
+ - & | ^ @ | add, subtract, bitwise and, bitwise or,
| bitwise xor, string concatenation
! | logical complement
< <= > >= == != | relational operators (perform both numeric
| and string comparisons)
&& || | logical and, logical or

NOTES: Unary minus is supported, but unary plus is not (thus -10 is valid while +10 is not).

The following examples illustrate the use of several of the basic operators.

For string concatenation, the ``@'' operator is used:

      genesis > echo {"sub" @ "genius"}
      subgenius

The following command shows how precedence is employed:

      genesis > echo { 24 / 12 + 7 * 2 }
      16

You can also explicitly specify the precedence of operations in an expression by using parentheses:

      genesis > echo { 3 * 4 + 5 }
      17
      genesis > echo { 3 * (4 + 5 ) }
      27

The following command does not return 5.4 because each element of the expression is an integer:

      genesis > echo { 9 * 3 / 5 }
      5

For comparisons in GENESIS, the integer 0 is considered False; all other integer values are considered True; GENESIS returns 1 to indicate True. The following command returns 1 because the expression is True (it would have returned 0 if the expression had been False):

      genesis > echo { 4 > 2 }
      1

Here is an example of negation in determining the nonexistence of an element:

      if (!{exists {dest}/soma})
         ...

The relational operators are defined to be non-associative. This prevents silly expressions like the following:

      1 < 2 < 4                                     [WRONG]

## 3.5 Control Structures

GENESIS supports four kinds of conditional control structures:

1. if/elif/else/end
2. while/end
3. for/end
4. foreach/end

Each of these control structures is briefly described here.

Note that you can break out of any of these loops before their expected termination by using the ``break'' or ``return'' statements.

### if/elif/else/end Structure

The GENESIS ``if ...'' conditional structure takes the following form:

        if (expression)
          statements
        elif (expression)
          statements
        else
          statements
        end

The elif and else portions of the form are optional and multiple elif portions may appear.

The ``if''/``elif'' expressions are evaluated in the order of appearance. When an expression evaluates ``true'', all the statements between the ``true'' expression and the following matching ``elif'', ``else'' or ``end'' will be executed; if all evaluated expressions are ``false'', then the statements between the ``else'' and the ``end'' are executed (or no statements are executed if there is no ``else'' clause).

Note that the GENESIS if construct does not recognize the keyword ``then''.

These are some examples of valid if statements:

      if(1)
        echo hello
      end

      if(1); echo hello; end

      if( (GRAPHICS == 1) || (5+3 > 10) )
        echo hello
      end

The following function uses the if/elif/else/end construct and prints ``zero'', ``negative'' or ``positive'' depending on the first argument:

        function iftest(arg)
            int arg
            if ( arg  == 0 )
                echo zero
            elif ( arg < 0 )
                 echo negative
            else
                 echo positive
            end
        end

### while/end Structure

The GENESIS ``while ...'' conditional looping statement has the following syntax:

       while (expression)
            statements
       end

The expression is evaluated. If it is ``true'' (i.e., not 0), then all the statements will be executed, then the expression will be evaluated again; if the evaluated expression is ``false'', then all the statements are skipped and control is turned to the first statement after the ``end''.

An example of a while loop is the following:

        str parent
        while({getfield {parent} object->name} != "xgraph")
            parent = {el {parent}/..}
        end

Note that the expression can be complicated and can involve some of the actual work of the loop.

### for/end

The GENESIS ``for ...'' conditional structure uses an incrementable variable in assessing the condition for continuing through the loop. The syntax of the loop is as follows:

        for (init_assignment; expression; incr_assignment)
          statements
        end

The init_assignment is an assignment statement which is evaluated once prior to entering the loop. The incr_assignment is an assignment statment which is executed following the statements each time through the loop. Otherwise, the for/end statement operates in the same manner as while/end.

To print the numbers one through ten:

        int i

        for (i = 1; i <= 10; i = i + 1)
            echo {i}
        end

### foreach/end

The GENESIS ``foreach'' statement loops through a set of statements once for each item in a specified argument list, having assigned the value of each item in the list to a looping variable for use by the statements:

      foreach loop-var ( arg1 [ arg ...] )
        statements
      end

For example:

      str s
      str thingys = "foo bar baz"
      foreach s ({arglist {thingys}})
          echo {s}
      end

or

      str name
      foreach name ({el {path}/##[OBJECT=xform]})
        xshow {name}
      end

The use of wildcards in the specification of pathnames is described in the section Hierarchical Structure. Also see the documentation for arglist and el (``element list''). Note that in each of these examples, you need to have declared the variables used in the loop before their use in the loop.

Previous Next Table of Contents