Eval Language of SteelBlue

Introduction

The Eval language of SteelBlue was created to facilitate numerical, string, and file manipulation that would otherwise not be possible with just the SteelBlue tags. If an ASSIGN tag contains the flag EVAL, then the contents of the VALUE flag is interpreted using the Eval language before the assignment is made. This allows things like variable incrementing:

 <ASSIGN NAME="$i" VALUE="$i + 1" EVAL>

Large blocks of Eval code can be placed in the EVAL container:

<EVAL>
 // The following code prints a hello message and today's date
 print("Hello World<br>\n");
 print("Today's date is: " . dtStrFromNow("%D") . "<br>\n");
</EVAL>

In addition, the Eval language is used to calculate the booleans in the BOOL flag of an IF tag and the CHECK flag of a checkbox formed by an INPUT tag.

There is a library of built-in functions that permit time, string, and file manipulation. Additional functions can be created through the DEFFUN tag or can be written directly in C++ into the file DynamicFuncEvaluator.cpp.

Syntax

The syntax of the Eval language is very similar to Perl. A section of Eval code is either a single statement or a block of statements separated by semicolons (;). For example, a simple EVAL program is:

 $a = 1;
 print($a);
which results in printing "1". Like C and Perl, the value of a block of statements is the value of the last statement. Therefore, the value of:
 $a = "snarf";
 length($a);
is 5.

Types of Statements

An Eval statement is either:

Constants

The Eval language supports two constants: strings and floats (which includes integers). A string constant is signified by any sequence of characters beginning and ending with either a single quote or a double quote. If a quote is to be used inside a string which has the same surrounding quotes, it can be escaped using the backslash (\). A blackslash in a string must be escaped for it to appear (\\). Therefore:
 print('This isn\'t to be believed.');
and
 print("This isn't to be believed.");
will both print This isn't to be believed. A backslash may also be used to generate the newline and tab character with "\n" and "\t" respectively. Like in Perl, a physical tab or newline character in a string will not be ignored. So:
 $newline="
";
 $another_newline = "\n";
will set both $newline and $another_newline to the newline character.

Numbers can be specified by strings, or by fixed point constants. If a string is used in an arithmetic expression, it is first converted into a number through the "atof(3)" C function call. The real number exponential format (E format) is only supported within a string. Therefore:

 print(1 + 2.5 + "3E1");
will print 33.5 . It should be noted that the Eval interpreter represents all numbers as strings in the environment.

Variables

In EVAL, as in SteelBlue, there are two types of variables: singleton variables (for example $a and $NumberOfHits), and associative arrays (for example $person{FirstName}). As in Perl, variables can be assigned integers, real numbers, and strings interchangeably. Singleton variables begin with $, and have names composed of alphanumeric characters and "_".

Associative arrays are in the form $namespace{key} where "namespace" is composed of alphanumeric characters and "_", and "key" is composed of either

Therefore, the following are legal associative array variables: Please note that these formats are legal in any SteelBlue flag in which variables are substituted.

Arithmetic Expressions

Arithmetic expressions are defined to be: Therefore (2 + 3) * length("hi") + -3 is an arithmetic expression that returns 7. The following tables show the unary and binary operators (please note that all boolean expressions return 0 as false and 1 as true and that anything numerically non-zero is considered true):

Unary Operators
! Boolean Not
- Unary minus: "- $n" is equivalent to "0 - $n"

Binary Operators
=Assignment: returns the value from the right side of the "="
*Numerical Multiplication
/Numerical Division
+Numerical Addition
-Numerical Subtraction
.String Concatenation
eqString Equals
neString Not Equals
==Numerical Equals
!=Numerical Not Equals
<Numerical Less Than
>Numerical Greater Than
<=Less Than or Equals
>=Greater Than or Equals
&Bitwise And
|Bitwise Or
&&Boolean And
||Boolean Or

With the exception of assignment (=), all operators are evaluated from left to right. Unlike mathematics in C or Perl, there are only four levels of precedence. Given from highest (first executed) to lowest these are:

Operator Precedence
Items in parentheses "()"
Unary - and !
*, /, &, and &&
=, +, -, ., eq, ne, !=, ==, <, >, <=, >=, |, and ||

This leads to pitfalls, as statements like:

 $i == 2 || $i == 3 // WRONG!
are improperly interpreted as:
 (($i == 2) || $i) == 3
Parentheses are required to make the statement correct:
 ($i == 2) || ($i == 3)

Assignments

In Eval, as in C, the single equals (=) is always used for variable assignment. Equality for numbers is tested by double equals (==), and equality for strings is tested by the function "eq". As a binary operator, assignments are interpreted from right to left, so arithmetic expressions like:
 $i = $i + 1;
work properly. This also allows multiple assignments such as the following:
 $a = $b = 0;
which sets both $a and $b to 0.

Functions

There are many built-in functions that permit manipulation of strings, numbers, and files. In addition, users can create their own functions through the DEFFUN tag, the function keyword, or if speed is greatly needed, as C++ in the file DynamicFuncEvaluator.cpp.

The syntax for creating a function in the Eval language is:

function [$return_var =] function_name($arg1,$arg2,...,$argn){
  eval commands...
}

For example, the following defines a function that can be used to subtract two dates (this is the same function defined in the Date Subtraction Example section:

 function $res = dateDiff($a,$b){
  $res = dtEpoc($a) - dtEpoc($b); 
 }
Because $res is declared as the return variable of the function, the value of $res when the code block terminates is returned as the value of the function. Another way to return values is through the return Eval function:
 function $res = dateDiff($a,$b){
  return(dtEpoc($a) - dtEpoc($b));
 }

When a function is executed, a new environment is created in which you can declare local variables with the function my. For example, the following SteelBlue code calculates the 10 numbers of the Fibanocci sequence using recursion:

/* FIB is a function that calculates the Fibanocci sequence using
   recursion.  It takes a single argument "a" which is an integer and
   returns the "a"th number in the sequence. */
function $res = fib($a){
  my ($x, $y);
  if ($a < 2){     // Base case: FIB(0) = FIB(1) = 1
    return(1);
  } else {         // Otherwise: FIB(a) = FIB(a-1) + FIB(a-2)
    $x = $a - 1;
    $y = $a - 2;
    return(FIB($x) + FIB($y));  // Recursion, yea!!!!
  };
}

for($i=0;$i<10;$i = $i + 1){
  print ("<br>\n");
  print("Fib(".$i.") = ".Fib($i));
  flush();
}

Functions with names of the form "ClassName::method" are special functions called Object Methods that are involved in defining Eval Class Objects. Functions with names of the form "~ClassName" are class deconstructors. In both class methods and class deconstructors, the variable $this is assigned a pointer to the calling instantiated object. Please see the Class Objects section for more information about Eval class objects.

Object Constructors

Class constructors are special functions that define an Eval class by defining class members (and specifying an optional inherited parent class). Class constructors are of the form:
class ClassName($arg1,$arg2,...,$argn) 
      [: ParentClassName($parg1,$parg2,...,$pargm)]{
  // Initialization of class members
  $this.member_a = ...;
  $this.member_b = ...;
  ...
}
In the class constructor the variable $this is assigned a pointer to the resulting instantiated object.

Please see the Class Objects section for more information about Eval class objects.

Object Instantiation

Built-in or user defined object classes are instantiated with the new keyword and are deleted with the delete function:
 $object_ref = new ClassName($arg1,$arg2,...,$argn);
 // Code that uses the object
 delete($object); // Automatically calls ~ClassName()
Please see the Class Objects section for more information about Eval class objects, and the Eval Class Reference for the list of built-in classes.

Object Members

Object members can be accessed through the syntax:
 $object_ref.member_name
You can treat most class members like any normal variable, in that you can assign them values, or take their values in Eval expressions. For built-in objects, a member can be public or private. Private members cannot be assigned values. However, members of an Eval defined class are always public (and therefore can be assigned).

Please see the Class Objects section for more information about Eval class objects, and the Eval Class Reference for the list of built-in classes.

Object Methods

Object methods can be accessed through the syntax:
 $object_ref.method_name($arg1,$arg2,...,$argn);
You can treat object methods like any function call.

Please see the Class Objects section for more information about Eval class objects, and the Eval Class Reference for the list of built-in classes.

Blocks

A block is a sequence of statements separated by semicolons (;) and surrounded by curly braces ({ and }). The value returned by a block is the value returned by the last statement. The following assigns $i to the value 4:
 $i = {$j = 1; $j + 3;};
Currently, blocks do not generate new environments like C. Therefore:
 my($a);
 $a = 2;
 {
   my($a);
   $a = 3;
 };
 print($a);
would print 3. Also note the semicolon after the block. Since a block is a statement, currently it requires a semicolon separating it from another statement directly following it. (Such semicolons after a block may not be required in the near future.) However, the last statement in a block does not require a semicolon after it.

If Statements

If statements are similar to C "if" statements. Curly brackets are required to allow more than one statement to be included in a "then" or "else" clause:
 if (group('admin'))
   print("User is an admin");
 else if (group('employee'))
   print("User is employee");
 else {
   print("User is nobody\n");
   print("Who are you?\n");
 }

While Loops

While loops are very similar to those in C or Perl. For example, the following code:

 // Display the Fibinocci number below 100
 $i=1; $j=1;
 while ($i < 100){
   print($i.", ");
   $k = $i + $j;
   $i = $j;
   $j = $k;
 }
 print("\n");
prints the numbers "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ".

For Loops

For loops are very similar to those in C or Perl. For example, the following code:

 // Display positive powers of 2 below 1000
 for($i = 1; $i < 1000; $i = 2 * $i)
  print($i.", ");
 print("\n");
prints the numbers "1, 2, 4, 8, 16, 32, 64, 128, 256, 512, ".

Comments

Both the multi-line and single-line format for C++ style comments are supported:
 /* This is a multi-line comment.  It starts with slash-star, and will
    continue from one line to the next until we hit star-slash */

 // This is a single-line comment

 $i = 0;
 print( /* multi-line comments can go anywhere */ "Hi".
       " world.".  // Single-line comments take up the rest of the line
       " This works!\n");

 /* This is a multi-line comment
    // This is not really a single-line comment since it is part of another
    And the multi-line comment ends here */

The above code prints "Hi world. This works!" since any comments outside of quotes are removed. As in C++, comments cannot be placed within strings since they will be considered part of the string. As can be seen in the above code, single-line comments can be surrounded by multi-line comments. However, like C++, the nesting of multi-line comments are not permitted.

Datatypes

Fundamentally, there are really only two datatypes in the Eval language (and for that matter in all of SteelBlue): strings and associative arrays. Techniques which convert strings and associative arrays to new datatypes allow the Eval language to use booleans, integers, real numbers, and arrays.

Strings

Strings are the fundamental datatype of the Eval language. In actuality, the contents of all variables are strings. For that reason, different operators must be used for addition (+) and concatenation (.) to determine if strings are to be treated as strings or as numbers:
 print("2" + "2");  // Prints 4
 print("2" . "2");  // Prints 22
Similarly, different boolean operators are required for string and numeric comparison. For example, the statement:
 "2" == "02"
is true since "==" is numeric equality, while the statement:
 "2" eq "02"
is false since "eq" is string equality.

Numbers

Both integers and real numbers are represented in Eval using strings. Strings are then converted back to floating point numbers with the C function atof(3) when needed. Strings beginning with a non-number (ignoring whitespace) are interpreted as "0". The following expression:
  2 + 0.1 + "1E-2"
evaluates to "2.11". If numbers can be represented as whole numbers, then the decimal values are dropped. So the expression:
 0.1 + 0.9
evaluates to "1".

Booleans

As in C, Booleans are represented as integers. However, in the Eval language, integers are represented as strings. Therefore, Booleans are strings in the Eval language. True is any value, that when interpreted as an integer, is non-zero. False is any value, that when interpreted as an integer, is zero. "1", "1.1", "-1", and "789" are interpreted to be true. "a", " ", "0", and "0.1" are interpreted to be false.

Associative Arrays

Associative arrays are variables that can take a string index within curly brackets immediately following the variable name. For example:
 $person{FirstName} = "Robert";
 $person{LastName} = "Buccigrossi";
 $person{"Full Name"} = $person{FirstName} . " " . $person{LastName};
 print($person{"Full Name"}); 
outputs "Robert Buccigrossi". The index can be any Eval expression that returns a string, or can be a string constant. As shown in the example, if the string constant is only composed of alphanumeric characters and underscores, the quotes are not required.

The function nextKey can be used to determine all the indices of an associative array:

 $a{hi} = "Hello";
 $a{there} = "There";
 $key = "";
 while(($key = nextKey($a,$key)) ne ""){
  print("$a{".$key."} = ".$a{$key}.", ");
 }
The above outputs "$a{hi} = Hello, $a{there} = There, ".

Arrays

Standard arrays are created by simple use of associative arrays. The consecutive integers starting at "0" are used as indices, and the string "LENGTH" is used as a key under which the number of array elements are kept. Therefore the SteelBlue code:
<accumulate name="a" value="red,blue,green" split=",">
<EVAL>
 $key = "";
 while(($key = nextKey($a,$key)) ne ""){
  print("$a{".$key."} = ".$a{$key}."<br>");
 }
</EVAL>
outputs
$a{0} = red
$a{1} = blue
$a{2} = green
$a{LENGTH} = 3

Object References

When a class is instantiated through the new keyword, the result is a pointer to an internal environment (similar to references in Perl). Because it is a pointer, any variable (and object method) can be assigned an object pointer. However, once delete is called on an object, all references to that object become invalid.

Please see the Class Objects section for more information about Eval class objects, and the Eval Class Reference for the list of built-in classes.

Class Objects

Class objects can be defined through the Eval language using object constructors, object methods, and object deconstructors. Classes can also be defined in C++, resulting in built-in Eval classes.

Class Definition Syntax

The syntax for defining a Class named "ClassName" in the Eval language is:

class ClassName($arg1,$arg2,...,$argn) 
      [: ParentClassName($parg1,$parg2,...,$pargm)]{
  // Initialization of class members
  $this.member_a = ...;
  $this.member_b = ...;
  ...
}

function [$ret_val =] ClassName::method_a($arg1,$arg2,...,$argn){
  // Function body
}

function [$ret_val =] ClassName::method_b($arg1,$arg2,...,$argn){
  // Function body
}

...

function ~ClassName(){ // Deconstructor
  // delete any members or do special tasks before object deletion
}

Methods and Members

Classes are composed of methods and members. A member is a property that a class has (for example the first and last name of a person) and is represented by a variable. A method is an action that can be done to a class or an action which a class does (for example displaying a graph, or setting the title of a graph).

For example, the built-in Draw class (through which you can create a drawing with text, lines, and boxes), has two members called "width" and "height" which control the width and height of the resulting drawing. The Draw class also has the methods drawLine($xa,$ya,$xb,$yb) that draws a line, and outputDrawing() that displays the drawing.

You can treat most class members like any normal variable, in that you can assign them values, or take their values in Eval expressions. For built-in objects, a member can be public or private. Private members cannot be assigned values. However, members of an Eval defined class are always public (and therefore can be assigned).

Members are created by assigning a value to the member in the constructor. For example, the following defines two members "first_name" and "last_name" in a "Person" class:

class Person($fname,$lname){
  $this.first_name = $fname;
  $this.last_name  = $lname;
}

$p = new Person("Rob","Buccigrossi");
print("The person is " . $p.first_name . " " . $p.last_name);
delete($p);

Methods are created by defining a function with a name of the form "ClassName::MethodName". In the body of the member function, $this is assigned a pointer to the calling instantiated object:

class Person($fname,$lname){
  $this.first_name = $fname;
  $this.last_name  = $lname;
}

function $res = Person::getFullName(){
  return($this.first_name." ".$this.last_name);
}

$p = new Person("Rob","Buccigrossi");
print("The person is " . $p.getFullName());
delete($p);

Class Inheritance

The methods and members of one class, called the "parent" class, can be inherited by another class, called the "child" class. This is done by specifying the parent class in the constructor of the child class. When the child class is instantiated, both the constuctor of the parent and child classes are called (the parent's constructor is called first. Therefore, the child class inherits all of the methods and members of the parent class (including the deconstructor). However, by defining a new copy of a method or member for the child class, the original parent class' method of member is overwritten. For example:
class Person($fname,$lname){
  $this.first_name = $fname;
  $this.last_name  = $lname;
}

function $res = Person::getFullName(){
  return($this.first_name." ".$this.last_name);
}

function $res = Person::getSalutation(){
  return("Dear ".$this.getFullName());
}

class PersonWithTitle($title,$fname,$lname)
   : Person($fname,$lname){
  $this.title = $title;
}

function $res = PersonWithTitle::getFullName(){
  return($this.title . " " . $this.first_name." ".$this.last_name);
}

$p = new PersonWithTitle("Dr.","Rob","Buccigrossi");
print("The person is " . $p.getFullName()."\n");
print("A salutation for this person is '" . $p.getSalutation()."'");
delete($p);
In this example "PersonWithTitle" inherits the members "first_name" and "last_name" and the method "getSalutation()" from the Person class. However, PersonWithTitle adds the member "title" and overwrites the method "getFullName()" so that it displays the title with the person's name.

This example also displays that methods and members are always "virtual", meaning that the method called is determined by the method's original class, not by the context in which the method is called. Specifically, notice that getSalutation() is defined only for the Person class, and calls the getFullName() method. But getFullName() is defined for both the Person and PersonWithTitle objects. Which version of getFullName is called? If the original object is a Person object, then Person::getFullName() is called. If the original object is a PersonWithTitle object, then PersonWithTitle::getFullName() is called. So the result of this code segment is:

The person is Dr. Rob Buccigrossi
A salutation for this person is 'Dear Dr. Rob Buccigrossi'

Please note that all Eval objects can be used as parent classes, even built-in Eval objects.

Class Deconstruction

A deconstructor is a special function that is called just before an object is deleted through the delete function. It is intended to do special clean-up (for example, disconnecting from servers, deleting allocated memory, etc.). To define a deconstructor, create a function with the name "~ClassName". For example:
class FTP(){
  $this.connected = 0;
}

function FTP::connect($server,$username,$password){
  // Commands to connect to the FTP Server
  $this.connected = 1;
}

function FTP::disconnect($server,$username,$password){
  // Commands to disconnect to the FTP Server
  $this.connected = 0;
}

// Other methods ...

// Deconstructor: disconnect if connected...
function ~FTP(){
 if ($this.connected){
  $this.disconnect();
 }
}

Please note that the deconstructor of a parent class is used of a deconstructor for a child class is not defined. If a child class deconstructor is defined, it is used instead of the deconstructor of the parent class.

Parse Tree for Eval Language

The following is the parse tree for the Eval language. Outside of strings, whitespace is ignored except to separate tokens, and comments are removed.
SBExp -> Aexp | Aexp; | Aexp; SBExp
Aexp -> Mexp | Mexp Aop Aexp
Aop -> = | + | - | . | eq | ne | != | == | < | > | >= | <= | '|' | '||'
Mexp -> Value | Value Mop Mexp | Mexp
Mop -> % | * | / | & | &&
Value -> Function | Number | String | ObjectCall | {SBExp} | (Aexp) |
         ! Value | - Value | ! Value
Function -> if(Aexp) Aexp | if(Aexp) Aexp [;]? else Aexp |
             Alphanum() | Alphanum(Arglist) | while(Aexp) Aexp |
             for(Arglist;Arglist;Arglist) |
             (function|class) [Variable =]? Alphanum (Arglist) {SBExp} |
             new Alphanum(Arglist)
Arglist -> Arg | Arg,Arglist
Variable -> $Alphanum | Variable{Alphanum} | Variable{Value} |
            Variable.ObjectCall
ObjectCall -> AlphaAlphanum |  AlphaAlphanum() |
            AlphaAlphanum(ArgList) | ObjectCall.ObjectCall
Number -> [0-9|.]+
String -> "[~"]+" | '[~']+'
Alphanum -> [0-9|a-z|A-Z|_|:|~]+
Alpha -> [a-z|A-Z|_]+