11.3. Literal Syntax and Evaluation

One of the most powerful features of an interpreter-based language such as Gamma is the ability to evaluate symbols and expressions at run-time. Gamma uses the # operator to indicate a literal expression. For those familiar with Lisp, this is equivalent to the forward quote syntax. Gamma also supports evaluation of sub-expressions, using the ` and @ operators. For more details on their use, see Quote Operators and further explanation below.

11.3.1. Literal Expressions

A literal expression is the expression that specifies an actual value rather than a function for creating the value. For example, the number 3 is a literal, where the expression ([plus ] 1 2) is not. Similarly, the string "hello there" is a literal, and the expression string("hello ", "there") is not, yet they produce equal values when evaluated.

Most object types in Gamma have Lisp and Gamma literal forms. You can create a valid object of some types (such as numbers, symbols, and strings) by reading a literal from a file or the command line. Other types (such as arrays, classes, and functions) are created by corresponding statements or functions.

See Literals in the Reference Manual for definitions, notations, and examples of literal expressions in Gamma.

11.3.2. Deferring Expression Evaluation

A Gamma expression preceded by the quote operator (#) will be taken literally, i.e., it will be protected from the evaluator. When the symbol containing this literal is evaluated, its contents are then interpreted. For example:

Gamma> x = #5 + 6;
(+ 5 6)
Gamma> #x;
x
Gamma> x;
(+ 5 6)
Gamma> eval (x);
11
	  

In the first case, the quote operator (#) protects the entire expression from the evaluator. That is, it protects everything to its right, all the way to the end of the expression (usually a semicolon or closed parenthesis). In the second case it is used to [quot ]produce[quot ] the literal symbol x. Then x is evaluated, returning its literal contents. Finally, the eval function is used to force execution of the literal contents of x. The eval function forces the resolution of variable references, as in this example:

Gamma> a = 1;
1;
Gamma> x = a + 5;
6;
Gamma> x = #a + 5;
(+ a  5)
Gamma> a = 10;
10
Gamma> eval (x);
15
	  

The literal is often used to delay the evaluation of an expression until an event is triggered. A good example is the add_set_function. This function takes two arguments. The first argument must be a symbol, so the # operator is used to prevent the required symbol from being evaluated. The second argument is simply any expression, most commonly a function. The add_set_function function sets the second argument to be evaluated when the first argument is changed:

Gamma> add_set_function (#a, #princ("My value = " ));
(princ "My value =")
Gamma> a = 21 / 3;
My value = 7
	  

In the following variation of the above example, a symbol used as an argument has been assigned a literal symbol, so that its evaluation will result in the desired symbol:

Gamma> x = #b;
b
Gamma> add_set_function (x, #princ("My value = " ));
(princ "My value =")
Gamma> b = 21 / 3;
My value = 7
	  

11.3.3. Literal Function Arguments

Some functions require arguments that are symbols. Normally, the arguments to a function are evaluated before the function is actually invoked. It is possible to cause a function's arguments not to be evaluated by using the exclamation modifier in the function declaration.

As an example, we can write our own version of the add_set_function function mentioned above:

Gamma> function my_add_set (!sym, !exp)
                {add_set_function(sym, exp);}
(defun my_add_set (&noeval sym &noeval exp) (add_set_function sym exp))
Gamma> my_add_set (c, princ("My value = "));
(princ "My value = ")
Gamma> c = 21 / 3;
My value = 7
	  

For more details on function arguments see Function Arguments in the Functions and Program chapter of this Guide.

11.3.4. Partially Evaluated Literal

Gamma supports evaluation of sub-expressions, allowing you to write expressions whose elements may or may not be evaluated. In the following example, we want to create a literal expression which will calculate a to the power of b, where b is a specific power, evaluated at the time the literal is defined. The operator ` is used like # to prevent evaluation of the expression, but it allows for exceptions. These exceptions, which will be evaluated, are denoted using the @ operator.

Gamma> b = 3;
3
Gamma> my_cube = `(pow (a, @b));
(pow a 3)
Gamma> a = 10;
10
Gamma> eval(my_cube);
1000
	  

In the following example, the timer event is used to demonstrate how the current value of a variable can be evaluated into a literal:

Gamma> a = "hello";
"hello"
Gamma> every (15, #princ(a,"\n"));
1
Gamma> next_event();
hello
nil
Gamma> a = "goodbye";
"goodbye"
Gamma> next_event();
goodbye
Gamma> cancel (1);
[866239787 836823463 15 ((princ a,"\n")) 1]
Gamma> every (15, list (#princ, a, "\n"));
2
Gamma> next_event();
goodbye
nil
Gamma> a = "no more";
"no more"
Gamma> next_event();
goodbye
nil
		

11.3.5. Constructing Variable Names at Run-time

Controlling when an expression is evaluated lets you generate the actual variable names at run-time. This can produce extremely concise code, particularly compared to the C language equivalent. In the following example, a set of simple objects each has a value. The object name and its value is entered. In a conventional language, we might search the array of objects to find the one with the given name, and then make the assignment. Gamma makes it possible to directly construct the variable reference using the set function, as follows:

Gamma> name = "fido";
"fido"
Gamma> value = "bites";
"bites"
Gamma> set(symbol(string(name)), value);
"bites"
Gamma> fido;
"bites"
	  

Note that the syntax does not accept the = assignment operator, so the functional form of the assignment operator: set must be used. Note also that we would probably use undefined_p to verify that the variable actually existed to avoid halting the program due to an undefined variable error. Although the example is trivial, this technique is very useful for constructing function references based on run-time data.

11.3.6. Literal Array Syntax

An array is defined in Gamma with the array function, which creates the array and sets the elements to the specified values.

Gamma uses the familiar square brackets [ ] syntax to reference array elements. Although Gamma has the functions aref and aset for reading and writing specified elements of the array, the square bracket syntax is normally used. An array is automatically re-sized if an element beyond its current size is set. Arrays do not have a type, and array elements can be of different types. Array elements can be set to literals (including expressions protected from evaluation), as in the following example:

Gamma> x = array (3, "hi");
[3 "hi"]
Gamma> x[3] = #a + 5;
(+ a 5)
Gamma> x;
[3 "hi" nil (+ a 5)]
Gamma> a = 8;
8;
Gamma> eval(x[3]);
13
	  

[Note]

Generally, literal arrays should be avoided except for static variables. A literal array is embedded into your code. If it is changed, then the code is effectively changed!