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.
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.
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.
Associative arrays are in the form $namespace{key} where "namespace" is composed of alphanumeric characters and "_", and "key" is composed of either
| 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 |
| eq | String Equals |
| ne | String 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) == 3Parentheses are required to make the statement correct:
($i == 2) || ($i == 3)
$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.
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.
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.
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_ref = new ClassName($arg1,$arg2,...,$argn); // Code that uses the object delete($object); // Automatically calls ~ClassName()
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).$object_ref.member_name
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.
You can treat object methods like any function call.$object_ref.method_name($arg1,$arg2,...,$argn);
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.
$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 (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");
}
// 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, ".
// 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, ".
/* 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.
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.9evaluates to "1".
$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, ".
<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
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 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
}
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 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 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.
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|_]+