ScriptEase vs. C

This chapter is for those who already know how to program in C. It describes those elements of ScriptEase that differ from standard C. If you don't already know how to program in C, then you should instead look at Chapter 2: The ScriptEase Language.

We assume that readers of this section already know C, therefore only those aspects of ScriptEase that differ from C are described herein. If it's not mentioned here, you may assume that ScriptEase behavior will be standard C behavior.

Deviations from C are a result of these two directives: Convenience and Safety. ScriptEase is different from C where the change makes ScriptEase more convenient for small programs, command-line code, or scripting files, or where unaltered C rules encourage coding that is potentially unsafe.

The ScriptEase Language

ScriptEase is C without Type Declarations and Pointers. If you already know C, and can remember to forget these two aspects of C, then you already know ScriptEase. If you were to take C code and delete all the lines, code-words, and symbols that either declare data types or explicitly point to data, then you would be left with ScriptEase code; and although you would be removing bytes of source code, you would not be removing capabilities.

All of the details below that compare ScriptEase against C follow from the general rule: ScriptEase is C without Type Declarations and Pointers.

Case Sensitivity

ScriptEase is not case sensitive. John is equivalent to jOHN which is equivalent to JoHn. This holds true for function and variable names.

Data Types

The only recognized data types are Float, Long, and Byte. The words "Float", "Long", and "Byte" do not appear in the source code; instead, the data types are determined by context. For instance, 6 is a Long, 6.6 is a Float, and `6' is a Byte. Byte is unsigned, and the other types are signed.

Automatic Type Declaration

There are no type declarators and no type casting. Types are determined from context. If the code says "i = 6" then i is a Long, unless a previous statement has indicated otherwise. For instance, this C code:

int Max(int a, int b)

{

int result;

result = ( a < b ) ? b : a ;

return result;

}

could become this ScriptEase code:

Max(a,b)

{

result = ( a < b ) ? b : a ;

return result;

}

Array Representation

Arrays are used in ScriptEase much like they are in C, except that they are stored differently: a first-order array (e.g., a string) is stored in consecutive bytes in memory, just as in C, but arrays of arrays are not in consecutive memory locations. The C declaration:

char c[3][3]; // this is the C version

would state that there are nine consecutive bytes in memory. In ScriptEase a similar statement such as:

c[2][2] = `A'; // this is the ScriptEase version

would tell you that there are (at least) three arrays of characters, and the third array of arrays has (at least) three characters in it; and although the characters in c[0] are in consecutive bytes, and the characters in c[1] are in consecutive bytes, the two arrays c[0] and c[1] are not necessarily adjacent in memory.

Automatic Array Allocation

Arrays are dynamic, and any index (positive or negative) into an array is always valid. If an element of an array is referred to, then ScriptEase must see to it that such an element will exist. For instance, if the first statement in the ScriptEase source code is:

foo[4] = 7

then the ScriptEase interpreter will make an array of 5 integers referred to by the variable foo. If a statement further on refers to foo[6] then the ScriptEase interpreter will expand foo, if it has to, to ensure that the element foo[6] exists. This works with negative indices as well. When you refer to foo[-10], then foo is grown in the other direction if it needs to be, but foo[4] will still refer to that "7" you put there earlier. Arrays can reach any dimension order, so foo[6][7][34][-1][4] is a valid variable.

Literal Strings

A literal string in ScriptEase is any byte array (string) appearing in source code within double- or back-tick quotes. The following show examples of what are and are not literal strings:

"dog"; // literal string (double quotes)

`dog'; // not a literal string (single quotes)

{ `d', `o', `g', `\0' }; // not a literal string (array initializa-

tion)

`dog`; // literal string (back-ticks)

Literal strings have special treatment for certain ScriptEase operations for these reasons:

In general, literal strings follow these two rules:

Assignment (=) of Literal Strings

When a literal string is assigned to a variable, a copy is made of that string and the variable is assigned the copy of the literal string. For example, this code:

for ( i = 0; i < 3; i++ ) {

str = "dog";

strcat(str,"house");

puts(str);

}

results in this output:

doghouse

doghouse

doghouse

A strict C interpretation of this code would not only overwrite memory, but would also generate the output:

doghouse

doghousehouse

doghousehousehouse

Comparisons (==, <=, <, !=, etc...) and Literal Strings

If both sides of a comparison operator are strings, and at least one of them is a literal string, then the comparison is performed as if you were using strcmp(). If one or both vars are a literal string, then the following comparison operation translation is performed:

lvar operator rvar strcmp(lvar,rvar) operator 0

These examples demonstrate how the literal string uses a strcmp()-like logic:

if ( animal == "dog" ) // i.e., if ( 0 == strcmp(animal,"dog") )

if ( animal < "dog" ) // i.e., if ( strcmp(animal,"dog") < 0 )

if ( "dog" <= animal ) // i.e., if ( strcmp("dog",animal) <= 0 )

In ScriptEase, this code:

animal = "dog";

if animal == "dog"

puts("hush puppy");

would print "hush puppy".

Function Parameters and Literal Strings

When a literal string is a parameter to a function, then it is always passed as a copy (strcpy()). For example, this code:

for ( i = 0; i < 3; i++ ) {

str = strcat("dog","house");

puts(str)

}

results in this output:

doghouse

doghouse

doghouse

Return and Literal Strings

When a literal string is returned from a function with the return statement, then it is returned as a copy (strcpy()) of the string. This code:

for ( i = 0; i < 3; i++ ) {

str = strcat(dog(),"house");

puts(str)

}

 

dog()

{

return "dog";

}

results in this output:

doghouse

doghouse

doghouse

Switch/Case and Literal Strings

If either the switch expression or the case expression are a literal string, then that case statement will match based on a string comparison (strcmp()) of the two variables. This may be used in code such as:

switch(strlwr(temp,argv[1])) {

case "add":{

DoTheAddThing();

break;

}

case "remove":{

DoTheRemoveThing();

break;

}

default:{

puts("Whaddya want?");

}

}

Structures

Structures are created dynamically, and their elements are not necessarily contiguous in memory. When the ScriptEase interpreter comes across the statement

foo.animal = "dog"

it creates a structure element of foo that is referred to as "animal" and is a an array of characters, and this "animal" variable is thereafter carried around with foo. The resulting code looks very much like regular C code, except that there is not a separate structure definition anywhere.

This C code:

struct Point {

int Row;

int Column;

};

 

struct Square {

struct Point BottomLeft;

struct Point TopRight;

};

 

void main()

{

struct Square sq;

int Area;

sq.BottomLeft.Row = 1;

sq.BottomLeft.Column = 15;

sq.TopRight.Row = 82;

sq.TopRight.Column = 120;

Area = AreaOfASquare(sq);

}

 

int AreaOfASquare(struct Square s)

{

int width, height;

width = s.TopRight.Column - s.BottomLeft.Column + 1;

height = s.TopRight.Row - s.BottomLeft.Row + 1;

return( width * height );

}

can be changed into the equivalent ScriptEase code simply by removing the declaration lines, resulting in:

main()

{

sq.BottomLeft.Row = 1;

sq.BottomLeft.Column = 15;

sq.TopRight.Row = 82;

sq.TopRight.Column = 120;

Area = AreaOfASquare(sq);

}

 

int AreaOfASquare(s)

{

width = s.TopRight.Column - s.BottomLeft.Column + 1;

height = s.TopRight.Row - s.BottomLeft.Row + 1;

return( width * height );

}

Structures can be passed, returned, and modified just as any other variable. Of course structures and arrays are independent, so you could very well have the statement

foo[8].animal.forge[3] = bil.bo

Some operations, such as addition, are not yet defined for structures.

Passing Variables by Reference

By default, LValues in ScriptEase are passed to functions by reference, so if the function alters a variable, then the variable in the calling function is altered as well IF IT IS AN LVALUE. So instead of this C code:

main()

{

CQuadrupleInPlace(&i);

.

.

}

 

void CQuadrupleInPlace(int *j)

{

*j *= 4;

}

 

the ScriptEase version would be:

main()

{

.

.

QuadrupleInPlace(i);

.

.

}

 

void QuadrupleInPlace(j)

{

j *= 4;

}

If the rare circumstance arises that you want to pass a copy of an LValue to a function, instead of passing the variable by reference, then you can use the "copy-of" operator "=". foo(=i) can be interpreted as saying "pass a value equal to i, but not i itself"; so that "foo(=i) ... foo(j) { j *= 4; }" would not change the value at i.

Note the following ScriptEase calls to QuadrupleInPlace() would be valid, but no value will have changed upon return from QuadrupleInPlace() because an LValue is not being passed:

QuadrupleInPlace(8);

QuadrupleInPlace(i+1);

QuadrupleInPlace(=1);

Data Pointers(*) and Addresses(&)

No pointers. None. The "*" symbol NEVER means "pointer" and the "&" symbol never means "address". This may at first cause seasoned C programmers to gasp in disbelief, but it turns out to be not such a big deal, these two operators are seldom missed, after considering these two rules:

Case Statements

Case statements in a switch statement may be a constant, a variable, or any other statement that can be evaluated to a variable. So you might see this ScriptEase code:

switch(i) {

case 4:

case foe():

case "thorax":

case sqrt(foe()):

case (PILLBOX * 3 - 2):

default:

}

As described in the Literal Strings section above, if either the switch expression or the case expression is a literal string, then the comparison is made based on the contents of the two arrays (i.e., as if the comparison is !strcmp(switch_expr,case_expr)).

Initialization: Code external to functions

All code that is not inside any function block is interpreted before main() is called. So the following ScriptEase code:

printf("first ");

main()

{

printf("third.");

}

printf("second ");

would result in the output "first second third."

Unnecessary tokens

If symbols are redundant, they are usually unnecessary in ScriptEase. This allows for more flexibility for the non-C-trained and also lets more code get in the tiny space available on the command line. (What good is a semicolon if it doesn't tell the compiler anything new?) So you can have the statement "foo()" as well as "foo();". It certainly doesn't hurt to have the semicolon there, especially when it can clarify a "return;" statement, for example, but it isn't necessary. Similarly, "(" and ")" are often unnecessary, so you may have "while a < b a++" as a complete statement.

#include

The #include statement has been enhanced so that you can use it only to include a designated part of a file, if that is all that is required. So we have:

#Include <filespec, FileExtension, LinePrefix, HeaderLine, FooterLine>

where:

filespec is the same as the C #include <filespec> statement

FileExtension is a file extension (such as BAT) that may be added to filespec (so batch files can say #include <%0,bat>"

LinePrefix specifies that only those lines in filespec that begin with LinePrefix will be source

HeaderLine and FooterLine specify that source will be read only from sections of filespec between HeaderLine and FooterLine.

If a full path is not specified, the ScriptEase interpreter searches for the file in various paths in this order:

A file will not be included more than once in the ScriptEase interpreter, and so if it has already been included, a second (or third) #include statement will have no effect.

Macros

Function macros are not currently supported. Since speed is not of primary importance, a macro gains little over a function call, so any macros may simply become functions.

Token replacement macros, such as:

#define NULL 0

are supported in ScriptEase.

Back-quote strings

The back-quote character (`), also known as a "back-tick" or "grave accent", can be used in ScriptEase in place of a double quote (") to specify a string where escape sequences are not to be translated. For example, here are two ways to describe the same file name:

"c:\\autoexec.bat" // traditional C method

`c:\autoexec.bat` // alternative ScriptEase method

Converting existing C code to ScriptEase

Converting existing C code to ScriptEase is mostly a process of deleting unnecessary text. You search on type declarations, such as "int", "float", "struct", "char", "[ ]", etc... and then delete these declaration strings. For example, these instances of C code (or C++ code) on the left can be replaced by the ScriptEase code on the right:

C

ScriptEase

int i;

i (or nothing at all)

int foo=3

foo =3

struct { int row; int col; }

/* nothing */

char name[] = "George"

name = "George"

int goo(int a, char *s, int c)

goo(a, buf, c)

int zoo[] = {1, 2, 3}

zoo = {1, 2, 3}

Much of this could be handled automatically with statements such as these:

#define int /* nothing */

#define char /* nothing */

#define float /* nothing */

The next step in converting C to ScriptEase is to search for the address and pointer operators (* and &). If the & and * work together so that the address of a variable is passed to a function, then both of these operators become unnecessary because ScriptEase passes lvars by reference. If the script still has *'s in it they usually refer to the 0th value of a pointer address, and so can be replaced with [0], as in "*foo = 4" replaced by "foo[0] = 4". Finally, the "->" operator for structures may need to be replaced by "." if a value that was passed by address is now being passed by reference.