diff options
Diffstat (limited to 'libslang/doc/text/slang.txt')
-rw-r--r-- | libslang/doc/text/slang.txt | 5642 |
1 files changed, 5642 insertions, 0 deletions
diff --git a/libslang/doc/text/slang.txt b/libslang/doc/text/slang.txt new file mode 100644 index 0000000..6867293 --- /dev/null +++ b/libslang/doc/text/slang.txt @@ -0,0 +1,5642 @@ + A Guide to the S-Lang Language + John E. Davis, davis@space.mit.edu + Mar 23, 2003 + ____________________________________________________________ + + Table of Contents + + + + Preface + + 1. A Brief History of S-Lang + 2. Acknowledgements + 2. Introduction + 3. Language Features + 4. Data Types and Operators + 5. Statements and Functions + 6. Error Handling + 7. Run-Time Library + 8. Input/Output + 9. Obtaining S-Lang + 9. Overview of the Language + 10. Variables and Functions + 11. Strings + 12. Referencing and Dereferencing + 13. Arrays + 14. Structures and User-Defined Types + 15. Namespaces + 15. Data Types and Literal Constants + 16. Predefined Data Types + 16.1 Integers + 16.2 Floating Point Numbers + 16.3 Complex Numbers + 16.4 Strings + 16.5 Null_Type + 16.6 Ref_Type + 16.7 Array_Type and Struct_Type + 16.8 DataType_Type Type + + 17. Typecasting: Converting from one Type to Another + + 17. Identifiers + + 17. Variables + + 17. Operators + 18. Unary Operators + 19. Binary Operators + 19.1 Arithmetic Operators + 19.2 Relational Operators + 19.3 Boolean Operators + 19.4 Bitwise Operators + 19.5 Namespace operator + 19.6 Operator Precedence + 19.7 Binary Operators and Functions Returning Multiple Values + + 20. Mixing Integer and Floating Point Arithmetic + 21. Short Circuit Boolean Evaluation + 21. Statements + 22. Variable Declaration Statements + 23. Assignment Statements + 24. Conditional and Looping Statements + 24.1 Conditional Forms + 24.1.1 if + 24.1.2 if-else + 24.1.3 !if + 24.1.4 orelse, andelse + 24.1.5 switch + 24.2 Looping Forms + 24.2.1 while + 24.2.2 do...while + 24.2.3 for + 24.2.4 loop + 24.2.5 for + 24.2.6 forever + 24.2.7 foreach + + 25. break, return, continue + + 25. Functions + 26. Declaring Functions + 27. Parameter Passing Mechanism + 28. Referencing Variables + 29. Functions with a Variable Number of Arguments + 30. Returning Values + 31. Multiple Assignment Statement + 32. Exit-Blocks + 32. Name Spaces + 32. Arrays + 33. Creating Arrays + 33.1 Range Arrays + 33.2 Creating arrays via the dereference operator + + 34. Reshaping Arrays + 35. Indexing Arrays + 36. Arrays and Variables + 37. Using Arrays in Computations + 37. Associative Arrays + 37. Structures and User-Defined Types + 38. Defining a Structure + 39. Accessing the Fields of a Structure + 40. Linked Lists + 41. Defining New Types + 41. Error Handling + 42. Error-Blocks + 43. Clearing Errors + 43. Loading Files: evalfile and autoload + 43. File Input/Output + 44. Input/Output via stdio + 44.1 Stdio Overview + 44.2 Stdio Examples + + 45. POSIX I/O + 46. Advanced I/O techniques + 46.1 Example: Reading /var/log/wtmp + 46.1 Debugging + 46.1 Regular Expressions + + 47. S-Lang RE Syntax + 48. Differences between S-Lang and egrep REs + 48. Future Directions + 48. Copyright + A. The GNU Public License + B. The Artistic License + + + ______________________________________________________________________ + + + + 1. Preface + + + + S-Lang is an interpreted language that was designed from the start to + be easily embedded into a program to provide it with a powerful + extension language. Examples of programs that use S-Lang as an + extension language include the jed text editor, the slrn newsreader, + and sldxe (unreleased), a numerical computation program. For this + reason, S-Lang does not exist as a separate application and many of + the examples in this document are presented in the context of one of + the above applications. + + S-Lang is also a programmer's library that permits a programmer to + develop sophisticated platform-independent software. In addition to + providing the S-Lang extension language, the library provides + facilities for screen management, keymaps, low-level terminal I/O, + etc. However, this document is concerned only with the extension + language and does not address these other features of the S-Lang + library. For information about the other components of the library, + the reader is referred to the The S-Lang Library Reference. + + + 1.1. A Brief History of S-Lang + + + + I first began working on S-Lang sometime during the fall of 1992. At + that time I was writing a text editor (jed), which I wanted to endow + with a macro language. It occured to me that an application- + independent language that could be embedded into the editor would + prove more useful because I could envision embedding it into other + programs. As a result, S-Lang was born. + + S-Lang was originally a stack language that supported a postscript- + like syntax. For that reason, I named it S-Lang, where the S was + supposed to emphasize its stack-based nature. About a year later, I + began to work on a preparser that would allow one to write using a + more traditional infix syntax making it easier to use for those + unfamiliar with stack based languages. Currently, the syntax of the + language resembles C, nevertheless some postscript-like features still + remain, e.g., the `%' character is still used as a comment delimiter. + + + + 1.2. Acknowledgements + + + + Since I first released S-Lang, I have received a lot feedback about + the library and the language from many people. This has given me the + opportunity and pleasure to interact with several people to make the + library portable and easy to use. In particular, I would like to + thank the following individuals: + + Luchesar Ionkov <lionkov@sf.cit.bg> for his comments and criticisms of + the syntax of the language. He was the person who made me realize + that the low-level byte-code engine should be totally type- + independent. He also improved the tokenizer and preparser and + impressed upon me that the language needed a grammar. + + Mark Olesen <olesen@weber.me.queensu.ca> for his many patches to + various aspects of the library and his support on AIX. He also + contributed a lot to the pre-processing (SLprep) routines. + + John Burnell <j.burnell@irl.cri.nz> for the OS/2 port of the video and + keyboard routines. He also made value suggestions regarding the + interpreter interface. + + Darrel Hankerson <hankedr@mail.auburn.edu> for cleaning up and + unifying some of the code and the makefiles. + + Dominik Wujastyk <ucgadkw@ucl.ac.uk> who was always willing to test + new releases of the library. + + Michael Elkins <me@muddcs.cs.hmc.edu> for his work on the curses + emulation. + + Ulli Horlacher <framstag@belwue.de> and Oezguer Kesim <kesim@math.fu- + berlin.de> for the S-Lang newsgroup and mailing list. + + Hunter Goatley, Andy Harper <Andy.Harper@kcl.ac.uk>, and Martin P.J. + Zinser <zinser@decus.decus.de> for their VMS support. + + Dave Sims <sims@usa.acsys.com> and Chin Huang <cthuang@vex.net> for + Windows 95 and Windows NT support. + + Lloyd Zusman <ljz@asfast.com> and Rich Roth <rich@on-the-net.com> for + creating and maintaining www.s-lang.org. + + I am also grateful to many other people who send in bug-reports and + bug-fixes, for without such community involvement, S-Lang would not be + as well-tested and stable as it is. Finally, I would like to thank my + wife for her support and understanding while I spent long weekend + hours developing the library. + + + + 2. Introduction + + + + S-Lang is a powerful interpreted language that may be embedded into an + application to make the application extensible. This enables the + application to be used in ways not envisioned by the programmer, thus + providing the application with much more flexibility and power. + Examples of applications that take advantage of the interpreter in + this way include the jed editor and the slrn newsreader. + + + 2.1. Language Features + + + The language features both global and local variables, branching and + looping constructs, user-defined functions, structures, datatypes, and + arrays. In addition, there is limited support for pointer types. The + concise array syntax rivals that of commercial array-based numerical + computing environments. + + + 2.2. Data Types and Operators + + + + The language provides built-in support for string, integer (signed and + unsigned long and short), double precision floating point, and double + precision complex numbers. In addition, it supports user defined + structure types, multi-dimensional array types, and associative + arrays. To facilitate the construction of sophisticated data + structures such as linked lists and trees, a `reference' type was + added to the language. The reference type provides much of the same + flexibility as pointers in other languages. Finally, applications + embedding the interpreter may also provide special application + specific types, such as the Mark_Type that the jed editor provides. + + The language provides standard arithmetic operations such as addition, + subtraction, multiplication, and division. It also provides support + for modulo arithmetic as well as operations at the bit level, e.g., + exclusive-or. Any binary or unary operator may be extended to work + with any data type. For example, the addition operator (+) has been + extended to work between string types to permit string concatenation. + + The binary and unary operators work transparently with array types. + For example, if a and b are arrays, then a + b produces an array whose + elements are the result of element by element addition of a and b. + This permits one to do vector operations without explicitly looping + over the array indices. + + + + 2.3. Statements and Functions + + + + The S-Lang language supports several types of looping constructs and + conditional statements. The looping constructs include while, + do...while, for, forever, loop, foreach, and _for. The conditional + statements include if, if-then-else, and !if. + + User defined functions may be defined to return zero, one, or more + values. Functions that return zero values are similar to `procedures' + in languages such as PASCAL. The local variables of a function are + always created on a stack allowing one to create recursive functions. + Parameters to a function are always passed by value and never by + reference. However, the language supports a reference data type that + allows one to simulate pass by reference. + + Unlike many interpreted languages, S-Lang allows functions to be + dynamically loaded (function autoloading). It also provides + constructs specifically designed for error handling and recovery as + well as debugging aids (e.g., function tracebacks). + + Functions and variables may be declared as private belonging to a + namespace associated with the compilation unit that defines the + function or variable. The ideas behind the namespace implementation + stems from the C language and should be quite familiar to any one + familiar with C. + + + + 2.4. Error Handling + + + + The S-Lang language defines a construct called an error-block that may + be used for error handling and recovery. When a non-fatal run-time + error is encountered, any error blocks that have been defined are + executed as the run-time stack unwinds. An error block can optionally + clear the error and the program will continue running after the + statement that triggered the error. This mechanism is somewhat + similar to try-catch in C++. + + + + 2.5. Run-Time Library + + + + Functions that compose the S-Lang run-time library are called + intrinsics. Examples of S-Lang intrinsic functions available to every + S-Lang application include string manipulation functions such as + strcat, strchop, and strcmp. The S-Lang library also provides + mathematical functions such as sin, cos, and tan; however, not all + applications enable the use of these intrinsics. For example, to + conserve memory, the 16 bit version of the jed editor does not provide + support for any mathematics other than simple integer arithmetic, + whereas other versions of the editor do support these functions. + + Most applications embedding the languages will also provide a set of + application specific intrinsic functions. For example, the jed editor + adds over 100 application specific intrinsic functions to the + language. Consult your application specific documentation to see what + additional intrinsics are supported. + + + + 2.6. Input/Output + + + The language supports C-like stdio input/output functions such as + fopen, fgets, fputs, and fclose. In addition it provides two + functions, message and error, for writing to the standard output + device and standard error. Specific applications may provide other + I/O mechanisms, e.g., the jed editor supports I/O to files via the + editor's buffers. + + 2.7. Obtaining S-Lang + + + + Comprehensive information about the library may be obtained via the + World Wide Web from http://www.s-lang.org. + + S-Lang as well as some programs that embed it are freely available via + anonymous ftp in the United States from + + o ftp://space.mit.edu/pub/davis. + + It is also available outside the United States from the following + mirror sites: + + o ftp://ftp.uni-stuttgart.de/pub/unix/misc/slang/ + + o ftp://ftp.fu-berlin.de/pub/unix/news/slrn/ + + o ftp://ftp.ntua.gr/pub/lang/slang/ + + The Usenet newsgroup alt.lang.s-lang was created for S-Lang + programmers to exchange information and share macros for the various + programs the embed the language. The newsgroup comp.editors can be a + useful resource for S-Lang macros for the jed editor. Similarly, slrn + users will find news.software.readers to be a valuable source of + information. + + Finally, two mailing lists dealing with the S-Lang library have been + created: + + o slang-announce@babayaga.math.fu-berlin.de + + o slang-workers@babayaga.math.fu-berlin.de + + The first list is for announcements of new releases of the library, + while the second list is intended for those who use the library for + their own code development. To subscribe to the announcement list, + send an email to slang-announce-subscribe@babayaga.math.fu- + berlin.de and include the word subscribe in the body of the + message. To subscribe to the developers list, use the address + slang-workers-subscribe@babayaga.math.fu-berlin.de. + + + + 3. Overview of the Language + + + + This purpose of this section is to give the reader a feel for the S- + Lang language, its syntax, and its capabilities. The information and + examples presented in this section should be sufficient to provide the + reader with the necessary background to understand the rest of the + document. + + + 3.1. Variables and Functions + + + + S-Lang is different from many other interpreted languages in the sense + that all variables and functions must be declared before they can be + used. + + Variables are declared using the variable keyword, e.g., + + + variable x, y, z; + + + + declares three variables, x, y, and z. Note the semicolon at the end + of the statement. All S-Lang statements must end in a semi-colon. + + Unlike compiled languages such as C, it is not necessary to specify + the data type of a S-Lang variable. The data type of a S-Lang + variable is determined upon assignment. For example, after execution + of the statements + + + x = 3; + y = sin (5.6); + z = "I think, therefore I am."; + + + + x will be an integer, y will be a double, and z will be a string. In + fact, it is even possible to re-assign x to a string: + + + x = "x was an integer, but now is a string"; + + + + Finally, one can combine variable declarations and assignments in the + same statement: + + + variable x = 3, y = sin(5.6), z = "I think, therefore I am."; + + + + Most functions are declared using the define keyword. A simple + example is + + + + define compute_average (x, y) + { + variable s = x + y; + return s / 2.0; + } + + + + which defines a function that simply computes the average of two num- + bers and returns the result. This example shows that a function con- + sists of three parts: the function name, a parameter list, and the + function body. + + The parameter list consists of a comma separated list of variable + names. It is not necessary to declare variables within a parameter + list; they are implicitly declared. However, all other local + variables used in the function must be declared. If the function + takes no parameters, then the parameter list must still be present, + but empty: + + + define go_left_5 () + { + go_left (5); + } + + + + The last example is a function that takes no arguments and returns no + value. Some languages such as PASCAL distinguish such objects from + functions that return values by calling these objects procedures. + However, S-Lang, like C, does not make such a distinction. + + The language permits recursive functions, i.e., functions that call + themselves. The way to do this in S-Lang is to first declare the + function using the form: + + define function-name (); + + + It is not necessary to declare a parameter list when declaring a func- + tion in this way. + + The most famous example of a recursive function is the factorial + function. Here is how to implement it using S-Lang: + + + define factorial (); % declare it for recursion + + define factorial (n) + { + if (n < 2) return 1; + return n * factorial (n - 1); + } + + + + This example also shows how to mix comments with code. S-Lang uses + the `%' character to start a comment and all characters from the com- + ment character to the end of the line are ignored. + + + + 3.2. Strings + + + + Perhaps the most appealing feature of any interpreted language is that + it frees the user from the responsibility of memory management. This + is particularly evident when contrasting how S-Lang handles string + variables with a lower level language such as C. Consider a function + that concatenates three strings. An example in S-Lang is: + + + define concat_3_strings (a, b, c) + { + return strcat (a, strcat (b, c)); + } + + + + This function uses the built-in strcat function for concatenating two + strings. In C, the simplest such function would look like: + + + char *concat_3_strings (char *a, char *b, char *c) + { + unsigned int len; + char *result; + len = strlen (a) + strlen (b) + strlen (c); + if (NULL == (result = (char *) malloc (len + 1))) + exit (1); + strcpy (result, a); + strcat (result, b); + strcat (result, c); + return result; + } + + + + Even this C example is misleading since none of the issues of memory + management of the strings has been dealt with. The S-Lang language + hides all these issues from the user. + + Binary operators have been defined to work with the string data type. + In particular the + operator may be used to perform string + concatenation. That is, one can use the + operator as an alternative + to strcat: + + + define concat_3_strings (a, b, c) + { + return a + b + c; + } + + + + See section ??? for more information about string variables. + + + + 3.3. Referencing and Dereferencing + + + The unary prefix operator, &, may be used to create a reference to an + object, which is similar to a pointer in other languages. References + are commonly used as a mechanism to pass a function as an argument to + another function as the following example illustrates: + + + define compute_functional_sum (funct) + { + variable i, s; + + s = 0; + for (i = 0; i < 10; i++) + { + s += (@funct)(i); + } + return s; + } + + variable sin_sum = compute_functional_sum (&sin); + variable cos_sum = compute_functional_sum (&cos); + + + + Here, the function compute_functional_sum applies the function speci- + fied by the parameter funct to the first 10 integers and returns the + sum. The two statements following the function definition show how + the sin and cos functions may be used. + + Note the @ operator in the definition of compute_functional_sum. It + is known as the dereference operator and is the inverse of the + reference operator. + + Another use of the reference operator is in the context of the fgets + function. For example, + + + define read_nth_line (file, n) + { + variable fp, line; + fp = fopen (file, "r"); + + while (n > 0) + { + if (-1 == fgets (&line, fp)) + return NULL; + n--; + } + return line; + } + + + + uses the fgets function to read the nth line of a file. In particu- + lar, a reference to the local variable line is passed to fgets, and + upon return line will be set to the character string read by fgets. + + Finally, references may be used as an alternative to multiple return + values by passing information back via the parameter list. The + example involving fgets presented above provided an illustration of + this. Another example is + + + + define set_xyz (x, y, z) + { + @x = 1; + @y = 2; + @z = 3; + } + variable X, Y, Z; + set_xyz (&X, &Y, &Z); + + + + which, after execution, results in X set to 1, Y set to 2, and Z set + to 3. A C programmer will note the similarity of set_xyz to the fol- + lowing C implementation: + + + void set_xyz (int *x, int *y, int *z) + { + *x = 1; + *y = 2; + *z = 3; + } + + + + 3.4. Arrays + + + The S-Lang language supports multi-dimensional arrays of all + datatypes. For example, one can define arrays of references to + functions as well as arrays of arrays. Here are a few examples of + creating arrays: + + + variable A = Integer_Type [10]; + variable B = Integer_Type [10, 3]; + variable C = [1, 3, 5, 7, 9]; + + + + The first example creates an array of 10 integers and assigns it to + the variable A. The second example creates a 2-d array of 30 integers + arranged in 10 rows and 3 columns and assigns the result to B. In the + last example, an array of 5 integers is assigned to the variable C. + However, in this case the elements of the array are initialized to the + values specified. This is known as an inline-array. + + S-Lang also supports something called an range-array. An example of + such an array is + + + variable C = [1:9:2]; + + + + This will produce an array of 5 integers running from 1 through 9 in + increments of 2. + + Arrays are passed by reference to functions and never by value. This + permits one to write functions which can initialize arrays. For + example, + + + define init_array (a) + { + variable i, imax; + + imax = length (a); + for (i = 0; i < imax; i++) + { + a[i] = 7; + } + } + + variable A = Integer_Type [10]; + init_array (A); + + + + creates an array of 10 integers and initializes all its elements to 7. + + There are more concise ways of accomplishing the result of the + previous example. These include: + + + variable A = [7, 7, 7, 7, 7, 7, 7, 7, 7, 7]; + variable A = Integer_Type [10]; A[[0:9]] = 7; + variable A = Integer_Type [10]; A[*] = 7; + + + + The second and third methods use an array of indices to index the + array A. In the second, the range of indices has been explicitly + specified, whereas the third example uses a wildcard form. See sec- + tion ??? for more information about array indexing. + + Although the examples have pertained to integer arrays, the fact is + that S-Lang arrays can be of any type, e.g., + + + variable A = Double_Type [10]; + variable B = Complex_Type [10]; + variable C = String_Type [10]; + variable D = Ref_Type [10]; + + + + create 10 element arrays of double, complex, string, and reference + types, respectively. The last example may be used to create an array + of functions, e.g., + + + D[0] = &sin; + D[1] = &cos; + + + + The language also defines unary, binary, and mathematical operations + on arrays. For example, if A and B are integer arrays, then A + B is + an array whose elements are the sum of the elements of A and B. A + trivial example that illustrates the power of this capability is + + variable X, Y; + X = [0:2*PI:0.01]; + Y = 20 * sin (X); + + + + which is equivalent to the highly simplified C code: + + + double *X, *Y; + unsigned int i, n; + + n = (2 * PI) / 0.01 + 1; + X = (double *) malloc (n * sizeof (double)); + Y = (double *) malloc (n * sizeof (double)); + for (i = 0; i < n; i++) + { + X[i] = i * 0.01; + Y[i] = 20 * sin (X[i]); + } + + + + 3.5. Structures and User-Defined Types + + + + A structure is similar to an array in the sense that it is a container + object. However, the elements of an array must all be of the same + type (or of Any_Type), whereas a structure is heterogeneous. As an + example, consider + + + variable person = struct + { + first_name, last_name, age + }; + variable bill = @person; + bill.first_name = "Bill"; + bill.last_name = "Clinton"; + bill.age = 51; + + + + In this example a structure consisting of the three fields has been + created and assigned to the variable person. Then an instance of this + structure has been created using the dereference operator and assigned + to bill. Finally, the individual fields of bill were initialized. + This is an example of an anonymous structure. + + A named structure is really a new data type and may be created using + the typedef keyword: + + + + typedef struct + { + first_name, last_name, age + } + Person_Type; + + variable bill = @Person_Type; + bill.first_name = "Bill"; + bill.last_name = "Clinton"; + bill.age = 51; + + + + The big advantage of creating a new type is that one can go on to cre- + ate arrays of the data type + + + variable People = Person_Type [100]; + People[0].first_name = "Bill"; + People[1].first_name = "Hillary"; + + + + The creation and initialization of a structure may be facilitated by a + function such as + + + define create_person (first, last, age) + { + variable person = @Person_Type; + person.first_name = first; + person.last_name = last; + person.age = age; + return person; + } + variable Bill = create_person ("Bill", "Clinton", 51); + + + + Other common uses of structures is the creation of linked lists, + binary trees, etc. For more information about these and other + features of structures, see section ???. + + + + 3.6. Namespaces + + + In addition to the global namespace, each compilation unit (e.g., a + file) is given a private namespace. A variable or function name that + is declared using the static keyword will be placed in the private + namespace associated with compilation unit. For example, + + + variable i; + static variable i; + + + + defines two variables called i. The first declaration defines i in + the global namespace, but the second declaration defines i in the pri- + vate namespace. + + The -> operator may be used in conjunction with the name of the + namespace to access objects in the name space. In the above example, + to access the variable i in the global namespace, one would use + Global->i. Unless otherwise specified, a private namespace has no + name and its objects may not be accessed from outside the compilation + unit. However, the implements function may be used give the private + namespace a name, allowing access to its objects. For example, if the + file t.sl contains + + + implements ("A"); + static variable i; + + + + then another file may access the variable i via A->i. + + + + 4. Data Types and Literal Constants + + + + The current implementation of the S-Lang language permits up to 256 + distinct data types, including predefined data types such as integer + and floating point, as well as specialized applications specific data + types. It is also possible to create new data types in the language + using the typedef mechanism. + + Literal constants are objects such as the integer 3 or the string + "hello". The actual data type given to a literal constant depends + upon the syntax of the constant. The following sections describe the + syntax of literals of specific data types. + + + 4.1. Predefined Data Types + + + + The current version of S-Lang defines integer, floating point, + complex, and string types. It also defines special purpose data types + such as Null_Type, DataType_Type, and Ref_Type. These types are + discussed below. + + + 4.1.1. Integers + + + + The S-Lang language supports both signed and unsigned characters, + short integer, long integer, and plain integer types. On most 32 bit + systems, there is no difference between an integer and a long integer; + however, they may differ on 16 and 64 bit systems. Generally + speaking, on a 16 bit system, plain integers are 16 bit quantities + with a range of -32767 to 32767. On a 32 bit system, plain integers + range from -2147483648 to 2147483647. + + An plain integer literal can be specified in one of several ways: + + o As a decimal (base 10) integer consisting of the characters 0 + through 9, e.g., 127. An integer specified this way cannot begin + with a leading 0. That is, 0127 is not the same as 127. + + o Using hexadecimal (base 16) notation consisting of the characters 0 + to 9 and A through F. The hexadecimal number must be preceded by + the characters 0x. For example, 0x7F specifies an integer using + hexadecimal notation and has the same value as decimal 127. + + o In Octal notation using characters 0 through 7. The Octal number + must begin with a leading 0. For example, 0177 and 127 represent + the same integer. + + Short, long, and unsigned types may be specified by using the + proper suffixes: L indicates that the integer is a long integer, h + indicates that the integer is a short integer, and U indicates that + it is unsigned. For example, 1UL specifies an unsigned long + integer. + + Finally, a character literal may be specified using a notation + containing a character enclosed in single quotes as 'a'. The value + of the character specified this way will lie in the range 0 to 256 + and will be determined by the ASCII value of the character in + quotes. For example, + + + i = '0'; + + + + assigns to i the character 48 since the '0' character has an ASCII + value of 48. + + Any integer may be preceded by a minus sign to indicate that it is a + negative integer. + + + + 4.1.2. Floating Point Numbers + + + + Single and double precision floating point literals must contain + either a decimal point or an exponent (or both). Here are examples of + specifying the same double precision point number: + + + 12. 12.0 12e0 1.2e1 120e-1 .12e2 0.12e2 + + + + Note that 12 is not a floating point number since it contains neither + a decimal point nor an exponent. In fact, 12 is an integer. + + One may append the f character to the end of the number to indicate + that the number is a single precision literal. + + + + 4.1.3. Complex Numbers + + + + The language implements complex numbers as a pair of double precision + floating point numbers. The first number in the pair forms the real + part, while the second number forms the imaginary part. That is, a + complex number may be regarded as the sum of a real number and an + imaginary number. + + Strictly speaking, the current implementation of the S-Lang does not + support generic complex literals. However, it does support imaginary + literals and a more generic complex number with a non-zero real part + may be constructed from the imaginary literal via addition of a real + number. + + An imaginary literal is specified in the same way as a floating point + literal except that i or j is appended. For example, + + + 12i 12.0i 12e0j + + + + all represent the same imaginary number. Actually, 12i is really an + imaginary integer except that S-Lang automatically promotes it to a + double precision imaginary number. + + A more generic complex number may be constructed from an imaginary + literal via addition, e.g., + + + 3.0 + 4.0i + + + + produces a complex number whose real part is 3.0 and whose imaginary + part is 4.0. + + The intrinsic functions Real and Imag may be used to retrieve the real + and imaginary parts of a complex number, respectively. + + + + 4.1.4. Strings + + + + A string literal must be enclosed in double quotes as in: + + + "This is a string". + + + + Although there is no imposed limit on the length of a string, string + literals must be less than 256 characters in length. It is possible + to go beyond this limit by string concatenation, e.g., + + + "This is the first part of a long string" + + "and this is the second half" + + + + Any character except a newline (ASCII 10) or the null character (ASCII + 0) may appear explicitly in a string literal. However, these charac- + ters may be used implicitly using the mechanism described below. + + The backslash character is a special character and is used to include + other special characters (such as a newline character) in the string. + The special characters recognized are: + + + \" -- double quote + \' -- single quote + \\ -- backslash + \a -- bell character (ASCII 7) + \t -- tab character (ASCII 9) + \n -- newline character (ASCII 10) + \e -- escape character (ASCII 27) + \xhhh -- character expressed in HEXADECIMAL notation + \ooo -- character expressed in OCTAL notation + \dnnn -- character expressed in DECIMAL + + + + For example, to include the double quote character as part of the + string, it must be preceded by a backslash character, e.g., + "This is a \"quote\"" + + + + Similarly, the next illustrates how a newline character may be + included: + + + "This is the first line\nand this is the second" + + + + 4.1.5. Null_Type + + + Objects of type Null_Type can have only one value: NULL. About the + only thing that you can do with this data type is to assign it to + variables and test for equality with other objects. Nevertheless, + Null_Type is an important and extremely useful data type. Its main + use stems from the fact that since it can be compared for equality + with any other data type, it is ideal to represent the value of an + object which does not yet have a value, or has an illegal value. + + As a trivial example of its use, consider + + + define add_numbers (a, b) + { + if (a == NULL) a = 0; + if (b == NULL) b = 0; + return a + b; + } + variable c = add_numbers (1, 2); + variable d = add_numbers (1, NULL); + variable e = add_numbers (1,); + variable f = add_numbers (,); + + + + It should be clear that after these statements have been executed, c + will have a value of 3. It should also be clear that d will have a + value of 1 because NULL has been passed as the second parameter. One + feature of the language is that if a parameter has been omitted from a + function call, the variable associated with that parameter will be set + to NULL. Hence, e and f will be set to 1 and 0, respectively. + + The Null_Type data type also plays an important role in the context of + structures. + + + 4.1.6. Ref_Type + + Objects of Ref_Type are created using the unary reference operator &. + Such objects may be dereferenced using the dereference operator @. + For example, + + + variable sin_ref = &sin; + variable y = (@sin_ref) (1.0); + + creates a reference to the sin function and assigns it to sin_ref. + The second statement uses the dereference operator to call the func- + tion that sin_ref references. + + The Ref_Type is useful for passing functions as arguments to other + functions, or for returning information from a function via its + parameter list. The dereference operator is also used to create an + instance of a structure. For these reasons, further discussion of + this important type can be found in section ??? and section ???. + + + 4.1.7. Array_Type and Struct_Type + + + Variables of type Array_Type and Struct_Type are known as container + objects. They are much more complicated than the simple data types + discussed so far and each obeys a special syntax. For these reasons + they are discussed in a separate chapters. See ???. + + + 4.1.8. DataType_Type Type + + + + S-Lang defines a type called DataType_Type. Objects of this type have + values that are type names. For example, an integer is an object of + type Integer_Type. The literals of DataType_Type include: + + + Char_Type (signed character) + UChar_Type (unsigned character) + Short_Type (short integer) + UShort_Type (unsigned short integer) + Integer_Type (plain integer) + UInteger_Type (plain unsigned integer) + Long_Type (long integer) + ULong_Type (unsigned long integer) + Float_Type (single precision real) + Double_Type (double precision real) + Complex_Type (complex numbers) + String_Type (strings, C strings) + BString_Type (binary strings) + Struct_Type (structures) + Ref_Type (references) + Null_Type (NULL) + Array_Type (arrays) + DataType_Type (data types) + + + + as well as the names of any other types that an application defines. + + The built-in function typeof returns the data type of its argument, + i.e., a DataType_Type. For instance typeof(7) returns Integer_Type + and typeof(Integer_Type) returns DataType_Type. One can use this + function as in the following example: + + + if (Integer_Type == typeof (x)) message ("x is an integer"); + + + + The literals of DataType_Type have other uses as well. One of the + most common uses of these literals is to create arrays, e.g., + x = Complex_Type [100]; + + + + creates an array of 100 complex numbers and assigns it to x. + + + + 4.2. Typecasting: Converting from one Type to Another + + + Occasionally, it is necessary to convert from one data type to + another. For example, if you need to print an object as a string, it + may be necessary to convert it to a String_Type. The typecast + function may be used to perform such conversions. For example, + consider + + + variable x = 10, y; + y = typecast (x, Double_Type); + + + + After execution of these statements, x will have the integer value 10 + and y will have the double precision floating point value 10.0. If + the object to be converted is an array, the typecast function will act + upon all elements of the array. For example, + + + variable x = [1:10]; % Array of integers + variable y = typecast (x, Double_Type); + + + + will create an array of 10 double precision values and assign it to y. + One should also realize that it is not always possible to perform a + typecast. For example, any attempt to convert an Integer_Type to a + Null_Type will result in a run-time error. + + Often the interpreter will perform implicit type conversions as + necessary to complete calculations. For example, when multiplying an + Integer_Type with a Double_Type, it will convert the Integer_Type to a + Double_Type for the purpose of the calculation. Thus, the example + involving the conversion of an array of integers to an array of + doubles could have been performed by multiplication by 1.0, i.e., + + + variable x = [1:10]; % Array of integers + variable y = 1.0 * x; + + + + The string intrinsic function is similar to the typecast function + except that it converts an object to a string representation. It is + important to understand that a typecast from some type to String_Type + is not the same as converting an object to its string operation. + That is, typecast(x,String_Type) is not equivalent to string(x). The + reason for this is that when given an array, the typecast function + acts on each element of the array to produce another array, whereas + the string function produces a a string. + The string function is useful for printing the value of an object. + This use is illustrated in the following simple example: + + + define print_object (x) + { + message (string (x)); + } + + + + Here, the message function has been used because it writes a string to + the display. If the string function was not used and the message + function was passed an integer, a type-mismatch error would have + resulted. + + + + 5. Identifiers + + + + The names given to variables, functions, and data types are called + identifiers. There are some restrictions upon the actual characters + that make up an identifier. An identifier name must start with a + letter ([A-Za-z]), an underscore character, or a dollar sign. The + rest of the characters in the name can be any combination of letters, + digits, dollar signs, or underscore characters. However, all + identifiers whose name begins with two underscore characters are + reserved for internal use by the interpreter and declarations of + objects with such names should be avoided. + + Examples of valid identifiers include: + + + mary _3 _this_is_ok + a7e1 $44 _44$_Three + + + + However, the following are not legal: + + + 7abc 2e0 #xx + + + + In fact, 2e0 actually specifies the real number 2.0. + + Although the maximum length of identifiers is unspecified by the + language, the length should be kept below 64 characters. + + The following identifiers are reserved by the language for use as + keywords: + + + !if _for do mod sign xor + ERROR_BLOCK abs do_while mul2 sqr public + EXIT_BLOCK and else not static private + USER_BLOCK0 andelse exch or struct + USER_BLOCK1 break for orelse switch + USER_BLOCK2 case foreach pop typedef + USER_BLOCK3 chs forever return using + USER_BLOCK4 continue if shl variable + __tmp define loop shr while + + + + In addition, the next major S-Lang release (v2.0) will reserve try and + catch, so it is probably a good idea to avoid those words until then. + + + + 6. Variables + + + + A variable must be declared before it can be used, otherwise an + undefined name error will be generated. A variable is declared using + the variable keyword, e.g, + + + variable x, y, z; + + + + declares three variables, x, y, and z. This is an example of a vari- + able declaration statement, and like all statements, it must end in a + semi-colon. + + Variables declared this way are untyped and inherit a type upon + assignment. The actual type checking is performed at run-time. For + example, + + + x = "This is a string"; + x = 1.2; + x = 3; + x = 2i; + + + + results in x being set successively to a string, a float, an integer, + and to a complex number (0+2i). Any attempt to use a variable before + it has acquired a type will result in an uninitialized variable error. + + It is legal to put executable code in a variable declaration list. + That is, + + + variable x = 1, y = sin (x); + + + + are legal variable declarations. This also provides a convenient way + of initializing a variable. + + Variables are classified as either global or local. A variable + declared inside a function is said to be local and has no meaning + outside the function. A variable is said to be global if it was + declared outside a function. Global variables are further classified + as being public, static, or private, according to the name space where + they were defined. See chapter ??? for more information about name + spaces. + + The following global variables are predefined by the language and are + mainly used as convenience variables: + + + $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 + + + + An intrinsic variable is another type of global variable. Such + variables have a definite type which cannot be altered. Variables of + this type may also be defined to be read-only, or constant variables. + An example of an intrinsic variable is PI which is a read-only double + precision variable with a value of approximately + 3.14159265358979323846. + + + + 7. Operators + + + + S-Lang supports a variety of operators that are grouped into three + classes: assignment operators, binary operators, and unary operators. + + An assignment operator is used to assign a value to a variable. They + will be discussed more fully in the context of the assignment + statement in section ???. + + An unary operator acts only upon a single quantity while a binary + operation is an operation between two quantities. The boolean + operator not is an example of an unary operator. Examples of binary + operators include the usual arithmetic operators +, -, *, and /. The + operator given by - can be either an unary operator (negation) or a + binary operator (subtraction); the actual operation is determined from + the context in which it is used. + + Binary operators are used in algebraic forms, e.g., a + b. Unary + operators fall in one of two classes: postfix-unary or prefix-unary. + For example, in the expression -x, the minus sign is a prefix-unary + operator. + + Not all data types have binary or unary operations defined. For + example, while String_Type objects support the + operator, they do not + admit the * operator. + + + 7.1. Unary Operators + + + The unary operators operate only upon a single operand. They include: + not, ~, -, @, &, as well as the increment and decrement operators ++ + and --, respectively. + + The boolean operator not acts only upon integers and produces 0 if its + operand is non-zero, otherwise it produces 1. + + The bit-level not operator ~ performs a similar function, except that + it operates on the individual bits of its integer operand. + + The arithmetic negation operator - is the most well-known unary + operator. It simply reverses the sign of its operand. + + The reference (&) and dereference (@) operators will be discussed in + greater detail in section ???. Similarly, the increment (++) and + decrement (--) operators will be discussed in the context of the + assignment operator. + + + 7.2. Binary Operators + + + + The binary operators may be grouped according to several classes: + arithmetic operators, relational operators, boolean operators, and + bitwise operators. + + All binary and unary operators may be overloaded. For example, the + arithmetic plus operator has been overloaded by the String_Type data + type to permit concatenation between strings. + + + + 7.2.1. Arithmetic Operators + + + + The arithmetic operators include +, -, *, /, which perform addition, + subtraction, multiplication, and division, respectively. In addition + to these, S-Lang supports the mod operator as well as the power + operator ^. + + The data type of the result produced by the use of one of these + operators depends upon the data types of the binary participants. If + they are both integers, the result will be an integer. However, if + the operands are not of the same type, they will be converted to a + common type before the operation is performed. For example, if one is + a floating point value and the other is an integer, the integer will + be converted to a float. In general, the promotion from one type to + another is such that no information is lost, if possible. As an + example, consider the expression 8/5 which indicates division of the + integer 8 by the integer 5. The result will be the integer 1 and not + the floating point value 1.6. However, 8/5.0 will produce 1.6 because + 5.0 is a floating point number. + + + + 7.2.2. Relational Operators + + + + The relational operators are >, >=, <, <=, ==, and !=. These perform + the comparisons greater than, greater than or equal, less than, less + than or equal, equal, and not equal, respectively. The result of one + of these comparisons is the integer 1 if the comparison is true, or 0 + if the comparison is false. For example, 6 >= 5 returns 1, but 6 == 5 + produces 0. + + + + 7.2.3. Boolean Operators + + + There are only two boolean binary operators: or and and. These + operators are defined only for integers and produce an integer result. + The or operator returns 1 if either of its operands are non-zero, + otherwise it produces 0. The and operator produces 1 if and only if + both its operands are non-zero, otherwise it produces 0. + + Neither of these operators perform the so-called boolean short-circuit + evaluation. For example, consider the expression: + + + (x != 0) and (1/x > 10) + + + + Here, if x were to have a value of zero, a division by zero error + would occur because even though x!=0 evaluates to zero, the and opera- + tor is not short-circuited and the 1/x expression would still be eval- + uated. Although these operators are not short-circuited, S-Lang does + have another mechanism of performing short-circuit boolean evaluation + via the orelse and andelse expressions. See below for information + about these constructs. + + + 7.2.4. Bitwise Operators + + + + The bitwise binary operators are defined only with integer operands + and are used for bit-level operations. Operators that fall in this + class include &, |, shl, shr, and xor. The & operator performs a + boolean AND operation between the corresponding bits of the operands. + Similarly, the | operator performs the boolean OR operation on the + bits. The bit-shifting operators shl and shr shift the bits of the + first operand by the number given by the second operand to the left or + right, respectively. Finally, the xor performs an EXCLUSIVE-OR + operation. + + These operators are commonly used to manipulate variables whose + individual bits have distinct meanings. In particular, & is usually + used to test bits, | can be used to set bits, and xor may be used to + flip a bit. + + As an example of using & to perform tests on bits, consider the + following: The jed text editor stores some of the information about a + buffer in a bitmapped integer variable. The value of this variable + may be retrieved using the jed intrinsic function getbuf_info, which + actually returns four quantities: the buffer flags, the name of the + buffer, directory name, and file name. For the purposes of this + section, only the buffer flags are of interest and can be retrieved + via a function such as + + + define get_buffer_flags () + { + variable flags; + (,,,flags) = getbuf_info (); + return flags; + } + + + + The buffer flags is a bitmapped quantity where the 0th bit indicates + whether or not the buffer has been modified, the first bit indicates + whether or not autosave has been enabled for the buffer, and so on. + Consider for the moment the task of determining if the buffer has been + modified. This can be determined by looking at the zeroth bit, if it + is 0 the buffer has not been modified, otherwise it has. Thus we can + create the function, + + + define is_buffer_modified () + { + variable flags = get_buffer_flags (); + return (flags & 1); + } + + + + where the integer 1 has been used since it has all of its bits set to + 0, except for the zeroth one, which is set to 1. (At this point, it + should also be apparent that bits are numbered from zero, thus an 8 + bit integer consists of bits 0 to 7, where 0 is the least significant + bit and 7 is the most significant one.) Similarly, we can create + another function + + + + define is_autosave_on () + { + variable flags = get_buffer_flags (); + return (flags & 2); + } + + + + to determine whether or not autosave has been turned on for the + buffer. + + The shl operator may be used to form the integer with only the nth bit + set. For example, 1 shl 6 produces an integer with all bits set to + zero except the sixth bit, which is set to one. The following example + exploits this fact: + + + define test_nth_bit (flags, nth) + { + return flags & (1 shl nth); + } + + + + 7.2.5. Namespace operator + + The operator -> is used to in conjunction with the name of a namespace + to access an object within the namespace. For example, if A is the + name of a namespace containing the variable v, then A->v refers to + that variable. + + + 7.2.6. Operator Precedence + + + + 7.2.7. Binary Operators and Functions Returning Multiple Values + + + Care must be exercised when using binary operators with an operand the + returns multiple values. In fact, the current implementation of the + S-Lang language will produce incorrect results if both operands of a + binary expression return multiple values. At most, only one of + operands of a binary expression can return multiple values, and that + operand must be the first one, not the second. For example, + + + define read_line (fp) + { + variable line, status; + + status = fgets (&line, fp); + if (status == -1) + return -1; + return (line, status); + } + + + + defines a function, read_line that takes a single argument, a handle + to an open file, and returns one or two values, depending upon the + return value of fgets. Now consider + + + while (read_line (fp) > 0) + { + text = (); + % Do something with text + . + . + } + + + + Here the relational binary operator > forms a comparison between one + of the return values (the one at the top of the stack) and 0. In + accordance with the above rule, since read_line returns multiple val- + ues, it occurs as the left binary operand. Putting it on the right as + in + + + while (0 < read_line (fp)) % Incorrect + { + text = (); + % Do something with text + . + . + } + + + + violates the rule and will result in the wrong answer. + + + + 7.3. Mixing Integer and Floating Point Arithmetic + + + If a binary operation (+, -, * , /) is performed on two integers, the + result is an integer. If at least one of the operands is a float, the + other is converted to float and the result is float. For example: + + + 11 / 2 --> 5 (integer) + 11 / 2.0 --> 5.5 (float) + 11.0 / 2 --> 5.5 (float) + 11.0 / 2.0 --> 5.5 (float) + + + + Finally note that only integers may be used as array indices, loop + control variables, and bit operations. The conversion functions, int + and float, may be used convert between floats and ints where appropri- + ate, e.g., + + + int (1.5) --> 1 (integer) + float(1.5) --> 1.5 (float) + float (1) --> 1.0 (float) + + 7.4. Short Circuit Boolean Evaluation + + + The boolean operators or and and are not short circuited as they are + in some languages. S-Lang uses orelse and andelse expressions for + short circuit boolean evaluation. However, these are not binary + operators. Expressions of the form: + + expr-1 and expr-2 and ... expr-n + + + can be replaced by the short circuited version using andelse: + + andelse {expr-1} {expr-2} ... {expr-n} + + + A similar syntax holds for the orelse operator. For example, consider + the statement: + + + if ((x != 0) and (1/x > 10)) do_something (); + + + + Here, if x were to have a value of zero, a division by zero error + would occur because even though x!=0 evaluates to zero, the and opera- + tor is not short circuited and the 1/x expression would be evaluated + causing division by zero. For this case, the andelse expression could + be used to avoid the problem: + + + if (andelse + {x != 0} + {1 / x > 10}) do_something (); + + + + 8. Statements + + + + Loosely speaking, a statement is composed of expressions that are + grouped according to the syntax or grammar of the language to express + a complete computation. Statements are analogous to sentences in a + human language and expressions are like phrases. All statements in + the S-Lang language must end in a semi-colon. + + A statement that occurs within a function is executed only during + execution of the function. However, statements that occur outside the + context of a function are evaluated immediately. + + The language supports several different types of statements such as + assignment statements, conditional statements, and so forth. These + are described in detail in the following sections. + + + 8.1. Variable Declaration Statements + + Variable declarations were already discussed in chapter ???. For the + sake of completeness, a variable declaration is a statement of the + form + + variable variable-declaration-list ; + + + where the variable-declaration-list is a comma separated list of one + or more variable names with optional initializations, e.g., + + + variable x, y = 2, z; + + + + 8.2. Assignment Statements + + + + Perhaps the most well known form of statement is the assignment + statement. Statements of this type consist of a left-hand side, an + assignment operator, and a right-hand side. The left-hand side must + be something to which an assignment can be performed. Such an object + is called an lvalue. + + The most common assignment operator is the simple assignment operator + =. Simple of its use include + + + x = 3; + x = some_function (10); + x = 34 + 27/y + some_function (z); + x = x + 3; + + + + In addition to the simple assignment operator, S-Lang also supports + the assignment operators += and -=. Internally, S-Lang transforms + + + a += b; + + + to + + + a = a + b; + + + + Similarly, a -= b is transformed to a = a - b. It is extremely impor- + tant to realize that, in general, a+b is not equal to b+a. This means + that a+=b is not the same as a=b+a. As an example consider + + + a = "hello"; a += "world"; + + + + After execution of these two statements, a will have the value "hel- + loworld" and not "worldhello". + + Since adding or subtracting 1 from a variable is quite common, S-Lang + also supports the unary increment and decrement operators ++, and --, + respectively. That is, for numeric data types, + + + x = x + 1; + x += 1; + x++; + + + + are all equivalent. Similarly, + + + x = x - 1; + x -= 1; + x--; + + + + are also equivalent. + + Strictly speaking, ++ and -- are unary operators. When used as x++, + the ++ operator is said to be a postfix-unary operator. However, when + used as ++x it is said to be a prefix-unary operator. The current + implementation does not distinguish between the two forms, thus x++ + and ++x are equivalent. The reason for this equivalence is that + assignment expressions do not return a value in the S-Lang language as + they do in C. Thus one should exercise care and not try to write C- + like code such as + + + x = 10; + while (--x) do_something (x); % Ok in C, but not in S-Lang + + + + The closest valid S-Lang form involves a comma-expression: + + + + x = 10; + while (x--, x) do_something (x); % Ok in S-Lang and in C + + + + S-Lang also supports a multiple-assignment statement. It is discussed + in detail in section ???. + + + + 8.3. Conditional and Looping Statements + + + + S-Lang supports a wide variety of conditional and looping statements. + These constructs operate on statements grouped together in blocks. A + block is a sequence of S-Lang statements enclosed in braces and may + contain other blocks. However, a block cannot include function + declarations. In the following, statement-or-block refers to either a + single S-Lang statement or to a block of statements, and integer- + expression is an integer-valued expression. next-statement represents + the statement following the form under discussion. + + + 8.3.1. Conditional Forms + + + + 8.3.1.1. if + + The simplest condition statement is the if statement. It follows the + syntax + + if (integer-expression) statement-or-block next-statement + + + If integer-expression evaluates to a non-zero result, then the state- + ment or group of statements implied statement-or-block will get exe- + cuted. Otherwise, control will proceed to next-statement. + + An example of the use of this type of conditional statement is + + + if (x != 0) + { + y = 1.0 / x; + if (x > 0) z = log (x); + } + + + + This example illustrates two if statements where the second if state- + ment is part of the block of statements that belong to the first. + + + 8.3.1.2. if-else + + Another form of if statement is the if-else statement. It follows the + syntax: + + if (integer-expression) statement-or-block-1 else statement-or-block-2 + next-statement + + Here, if expression returns non-zero, statement-or-block-1 will get + executed and control will pass on to next-statement. However, if + expression returns zero, statement-or-block-2 will get executed before + continuing with next-statement. A simple example of this form is + + + if (x > 0) z = log (x); else error ("x must be positive"); + + + + Consider the more complex example: + + + if (city == "Boston") + if (street == "Beacon") found = 1; + else if (city == "Madrid") + if (street == "Calle Mayor") found = 1; + else found = 0; + + + + This example illustrates a problem that beginners have with if-else + statements. The grammar presented above shows that the this example + is equivalent to + + + if (city == "Boston") + { + if (street == "Beacon") found = 1; + else if (city == "Madrid") + { + if (street == "Calle Mayor") found = 1; + else found = 0; + } + } + + + + It is important to understand the grammar and not be seduced by the + indentation! + + + 8.3.1.3. !if + + + One often encounters if statements similar to + + if (integer-expression == 0) statement-or-block + + + or equivalently, + + if (not(integer-expression)) statement-or-block + + + The !if statement was added to the language to simplify the handling + of such statements. It obeys the syntax + + !if (integer-expression) statement-or-block + + + and is functionally equivalent to + + if (not (expression)) statement-or-block + + + + 8.3.1.4. orelse, andelse + + + These constructs were discussed earlier. The syntax for the orelse + statement is: + + orelse {integer-expression-1} ... {integer-expression-n} + + + This causes each of the blocks to be executed in turn until one of + them returns a non-zero integer value. The result of this statement + is the integer value returned by the last block executed. For exam- + ple, + + + orelse { 0 } { 6 } { 2 } { 3 } + + + + returns 6 since the second block is the first to return a non-zero + result. The last two block will not get executed. + + The syntax for the andelse statement is: + + andelse {integer-expression-1} ... {integer-expression-n} + + + Each of the blocks will be executed in turn until one of them returns + a zero value. The result of this statement is the integer value + returned by the last block executed. For example, + + + andelse { 6 } { 2 } { 0 } { 4 } + + + + returns 0 since the third block will be the last to execute. + + + 8.3.1.5. switch + + The switch statement deviates the most from its C counterpart. The + syntax is: + + + switch (x) + { ... : ...} + . + . + { ... : ...} + + + + The `:' operator is a special symbol which means to test the top item + on the stack, and if it is non-zero, the rest of the block will get + executed and control will pass out of the switch statement. Other- + wise, the execution of the block will be terminated and the process + will be repeated for the next block. If a block contains no : opera- + tor, the entire block is executed and control will pass onto the next + statement following the switch statement. Such a block is known as + the default case. + + As a simple example, consider the following: + + + switch (x) + { x == 1 : message("Number is one.");} + { x == 2 : message("Number is two.");} + { x == 3 : message("Number is three.");} + { x == 4 : message("Number is four.");} + { x == 5 : message("Number is five.");} + { message ("Number is greater than five.");} + + + + Suppose x has an integer value of 3. The first two blocks will termi- + nate at the `:' character because each of the comparisons with x will + produce zero. However, the third block will execute to completion. + Similarly, if x is 7, only the last block will execute in full. + + A more familiar way to write the previous example used the case + keyword: + + + switch (x) + { case 1 : print("Number is one.");} + { case 2 : print("Number is two.");} + { case 3 : print("Number is three.");} + { case 4 : print("Number is four.");} + { case 5 : print("Number is five.");} + { print ("Number is greater than five.");} + + + + The case keyword is a more useful comparison operator because it can + perform a comparison between different data types while using == may + result in a type-mismatch error. For example, + + + switch (x) + { (x == 1) or (x == "one") : print("Number is one.");} + { (x == 2) or (x == "two") : print("Number is two.");} + { (x == 3) or (x == "three") : print("Number is three.");} + { (x == 4) or (x == "four") : print("Number is four.");} + { (x == 5) or (x == "five") : print("Number is five.");} + { print ("Number is greater than five.");} + + + + will fail because the == operation is not defined between strings and + integers. The correct way to write this to use the case keyword: + + + switch (x) + { case 1 or case "one" : print("Number is one.");} + { case 2 or case "two" : print("Number is two.");} + { case 3 or case "three" : print("Number is three.");} + { case 4 or case "four" : print("Number is four.");} + { case 5 or case "five" : print("Number is five.");} + { print ("Number is greater than five.");} + + + 8.3.2. Looping Forms + + + + 8.3.2.1. while + + The while statement follows the syntax + + while (integer-expression) statement-or-block next-statement + + + It simply causes statement-or-block to get executed as long as inte- + ger-expression evaluates to a non-zero result. For example, + + + i = 10; + while (i) + { + i--; + newline (); + } + + + + will cause the newline function to get called 10 times. However, + + + i = -10; + while (i) + { + i--; + newline (); + } + + + + would loop forever (or until i wraps from the most negative integer + value to the most positive and then decrements to zero). + + + If you are a C programmer, do not let the syntax of the language + seduce you into writing this example as you would in C: + + + i = 10; + while (i--) newline (); + + + + The fact is that expressions such as i-- do not return a value in S- + Lang as they do in C. If you must write this way, use the comma oper- + ator as in + + + i = 10; + while (i, i--) newline (); + + + + 8.3.2.2. do...while + + The do...while statement follows the syntax + + do statement-or-block while (integer-expression); + + + The main difference between this statement and the while statement is + that the do...while form performs the test involving integer-expres- + sion after each execution of statement-or-block rather than before. + This guarantees that statement-or-block will get executed at least + once. + + A simple example from the jed editor follows: + + + bob (); % Move to beginning of buffer + do + { + indent_line (); + } + while (down (1)); + + + + This will cause all lines in the buffer to get indented via the jed + intrinsic function indent_line. + + + 8.3.2.3. for + + Perhaps the most complex looping statement is the for statement; + nevertheless, it is a favorite of many programmers. This statement + obeys the syntax + + for (init-expression; integer-expression; end-expression) statement- + or-block next-statement + + + In addition to statement-or-block, its specification requires three + other expressions. When executed, the for statement evaluates init- + expression, then it tests integer-expression. If integer-expression + returns zero, control passes to next-statement. Otherwise, it exe- + cutes statement-or-block as long as integer-expression evaluates to a + non-zero result. After every execution of statement-or-block, end- + expression will get evaluated. + + This statement is almost equivalent to + + init-expression; while (integer-expression) { statement-or-block end- + expression; } + + + The reason that they are not fully equivalent involves what happens + when statement-or-block contains a continue statement. + + Despite the apparent complexity of the for statement, it is very easy + to use. As an example, consider + + + s = 0; + for (i = 1; i <= 10; i++) s += i; + + + + which computes the sum of the first 10 integers. + + + 8.3.2.4. loop + + The loop statement simply executes a block of code a fixed number of + times. It follows the syntax + + loop (integer-expression) statement-or-block next-statement + + + If the integer-expression evaluates to a positive integer, statement- + or-block will get executed that many times. Otherwise, control will + pass to next-statement. + + For example, + + + loop (10) newline (); + + + + will cause the function newline to get called 10 times. + + + 8.3.2.5. _.ds h for loop + + Like loop, the _for statement simply executes a block of code a fixed + number times. Unlike the loop statement, the _for loop is useful in + situations where the loop index is needed. It obeys the syntax + + _for (first-value, last-value, increment) block next-statement + + + Each time through the loop, the current value of the loop index is + pushed onto the stack. The first time through, the loop index will + have the value of first-value. The second time its value will be + first-value + increment, and so on. The loop will terminate when the + value of the loop index exceeds last-value. The current implementa- + tion requires the control parameters first-value, last-value, and + increment to be integered valued expressions. + + For example, it may be used to compute the sum of the first ten + integers: + + + s = 0; + _for (1, 10, 1) + { + i = (); + s += i; + } + + + + The execution speed of the _for loop is more than twice as fast as the + more powerful for loop making it a better choice for many situations. + + + 8.3.2.6. forever + + The forever statement is similar to the loop statement except that it + loops forever, or until a break or a return statement is executed. It + obeys the syntax + forever statement-or-block + + + A trivial example of this statement is + + + n = 10; + forever + { + if (n == 0) break; + newline (); + n--; + } + + + + 8.3.2.7. foreach + + The foreach statement is used to loop over one or more statements for + every element in a container object. A container object is a data + type that consists of other types. Examples include both ordinary and + associative arrays, structures, and strings. Every time through the + loop the current member of the object is pushed onto the stack. + + The simple type of foreach statement obeys the syntax + + foreach (container-object) statement-or-block + + + Here container-object can be an expression that returns a container + object. A simple example is + + + foreach (["apple", "peach", "pear"]) + { + fruit = (); + process_fruit (fruit); + } + + + + This example shows that if the container object is an array, then suc- + cessive elements of the array are pushed onto the stack prior to each + execution cycle. If the container object is a string, then successive + characters of the string are pushed onto the stack. + + What actually gets pushed onto the stack may be controlled via the + using form of the foreach statement. This more complex type of + foreach statement follows the syntax + + foreach ( container-object ) using ( control-list ) statement-or-block + + + The allowed values of control-list will depend upon the type of con- + tainer object. For associative arrays (Assoc_Type), control-list + specified whether keys, values, or both are pushed onto the stack. + For example, + + + + foreach (a) using ("keys") + { + k = (); + . + . + } + + + + results in the keys of the associative array a being pushed on the + list. However, + + + foreach (a) using ("values") + { + v = (); + . + . + } + + + + will cause the values to be used, and + + + foreach (a) using ("keys", "values") + { + (k,v) = (); + . + . + } + + + + will use both the keys and values of the array. + + Similarly, for linked-lists of structures, one may walk the list via + code like + + + foreach (linked_list) using ("next") + { + s = (); + . + . + } + + + + This foreach statement is equivalent + + + s = linked_list; + while (s != NULL) + { + . + . + s = s.next; + } + + + + Consult the type-specific documentation for a discussion of the using + control words, if any, appropriate for a given type. + + + 8.4. break, return, continue + + + S-Lang also includes the non-local transfer functions return, break, + and continue. The return statement causes control to return to the + calling function while the break and continue statements are used in + the context of loop structures. Consider: + + + define fun () + { + forever + { + s1; + s2; + .. + if (condition_1) break; + if (condition_2) return; + if (condition_3) continue; + .. + s3; + } + s4; + .. + } + + + + Here, a function fun has been defined that contains a forever loop + consisting of statements s1, s2,...,s3, and three if statements. As + long as the expressions condition_1, condition_2, and condition_3 + evaluate to zero, the statements s1, s2,...,s3 will be repeatedly exe- + cuted. However, if condition_1 returns a non-zero value, the break + statement will get executed, and control will pass out of the forever + loop to the statement immediately following the loop which in this + case is s4. Similarly, if condition_2 returns a non-zero number, the + return statement will cause control to pass back to the caller of fun. + Finally, the continue statement will cause control to pass back to the + start of the loop, skipping the statement s3 altogether. + + + + 9. Functions + + + + A function may be thought of as a group of statements that work + together to perform a computation. While there are no imposed limits + upon the number statements that may occur within a function, it is + considered poor programming practice if a function contains many + statements. This notion stems from the belief that a function should + have a simple, well defined purpose. + + + 9.1. Declaring Functions + + + + Like variables, functions must be declared before they can be used. + The define keyword is used for this purpose. For example, + + + define factorial (); + + + + is sufficient to declare a function named factorial. Unlike the vari- + able keyword used for declaring variables, the define keyword does not + accept a list of names. + + Usually, the above form is used only for recursive functions. In most + cases, the function name is almost always followed by a parameter list + and the body of the function: + + define function-name (parameter-list) { statement-list } + + + The function-name is an identifier and must conform to the naming + scheme for identifiers discussed in chapter ???. The parameter-list + is a comma-separated list of variable names that represent parameters + passed to the function, and may be empty if no parameters are to be + passed. The body of the function is enclosed in braces and consists + of zero or more statements (statement-list). + + The variables in the parameter-list are implicitly declared, thus, + there is no need to declare them via a variable declaration statement. + In fact any attempt to do so will result in a syntax error. + + + + 9.2. Parameter Passing Mechanism + + + + Parameters to a function are always passed by value and never by + reference. To see what this means, consider + + + define add_10 (a) + { + a = a + 10; + } + variable b = 0; + add_10 (b); + + + Here a function add_10 has been defined, which when executed, adds 10 + to its parameter. A variable b has also been declared and initialized + to zero before it is passed to add_10. What will be the value of b + after the call to add_10? If S-Lang were a language that passed + parameters by reference, the value of b would be changed to 10. How- + ever, S-Lang always passes by value, which means that b would retain + its value of zero after the function call. + + S-Lang does provide a mechanism for simulating pass by reference via + the reference operator. See the next section for more details. + + If a function is called with a parameter in the parameter list + omitted, the corresponding variable in the function will be set to + NULL. To make this clear, consider the function + + + define add_two_numbers (a, b) + { + if (a == NULL) a = 0; + if (b == NULL) b = 0; + return a + b; + } + + + + This function must be called with two parameters. However, we can + omit one or both of the parameters by calling it in one of the follow- + ing ways: + + + variable s = add_two_numbers (2,3); + variable s = add_two_numbers (2,); + variable s = add_two_numbers (,3); + variable s = add_two_numbers (,); + + + + The first example calls the function using both parameters; however, + at least one of the parameters was omitted in the other examples. The + interpreter will implicitly convert the last three examples to + + + variable s = add_two_numbers (2, NULL); + variable s = add_two_numbers (NULL, 3); + variable s = add_two_numbers (NULL, NULL); + + + + It is important to note that this mechanism is available only for + function calls that specify more than one parameter. That is, + + + variable s = add_10 (); + + + + is not equivalent to add_10(NULL). The reason for this is simple: the + parser can only tell whether or not NULL should be substituted by + looking at the position of the comma character in the parameter list, + and only function calls that indicate more than one parameter will use + a comma. A mechanism for handling single parameter function calls is + described in the next section. + 9.3. Referencing Variables + + + + One can achieve the effect of passing by reference by using the + reference (&) and dereference (@) operators. Consider again the add_10 + function presented in the previous section. This time we write it as + + + define add_10 (a) + { + @a = @a + 10; + } + variable b = 0; + add_10 (&b); + + + + The expression &b creates a reference to the variable b and it is the + reference that gets passed to add_10. When the function add_10 is + called, the value of a will be a reference to b. It is only by deref- + erencing this value that b can be accessed and changed. So, the + statement @a=@a+10; should be read `add 10' to the value of the object + that a references and assign the result to the object that a refer- + ences. + + The reader familiar with C will note the similarity between references + in S-Lang and pointers in C. + + One of the main purposes for references is that this mechanism allows + reference to functions to be passed to other functions. As a simple + example from elementary calculus, consider the following function + which returns an approximation to the derivative of another function + at a specified point: + + + define derivative (f, x) + { + variable h = 1e-6; + return ((@f)(x+h) - (@f)(x)) / h; + } + + + + It can be used to differentiate the function + + + define x_squared (x) + { + return x^2; + } + + + + at the point x = 3 via the expression derivative(&x_squared,3). + + + + 9.4. Functions with a Variable Number of Arguments + + + + S-Lang functions may be defined to take a variable number of + arguments. The reason for this is that the calling routine pushes the + arguments onto the stack before making a function call, and it is up + to the called function to pop the values off the stack and make + assignments to the variables in the parameter list. These details + are, for the most part, hidden from the programmer. However, they are + important when a variable number of arguments are passed. + + Consider the add_10 example presented earlier. This time it is + written + + + define add_10 () + { + variable x; + x = (); + return x + 10; + } + variable s = add_10 (12); % ==> s = 22; + + + + For the uninitiated, this example looks as if it is destined for dis- + aster. The add_10 function looks like it accepts zero arguments, yet + it was called with a single argument. On top of that, the assignment + to x looks strange. The truth is, the code presented in this example + makes perfect sense, once you realize what is happening. + + First, consider what happened when add_10 is called with the the + parameter 12. Internally, 12 is pushed onto the stack and then the + function called. Now, consider the function itself. x is a variable + local to the function. The strange looking assignment `x=()' simply + takes whatever is on the stack and assigns it to x. In other words, + after this statement, the value of x will be 12, since 12 will be at + the top of the stack. + + A generic function of the form + + + define function_name (x, y, ..., z) + { + . + . + } + + + + is internally transformed by the interpreter to + + + + define function_name () + { + variable x, y, ..., z; + z = (); + . + . + y = (); + x = (); + . + . + } + + + + before further parsing. (The add_10 function, as defined above, is + already in this form.) With this knowledge in hand, one can write a + function that accepts a variable number of arguments. Consider the + function: + + + define average_n (n) + { + variable x, y; + variable s; + + if (n == 1) + { + x = (); + s = x; + } + else if (n == 2) + { + y = (); + x = (); + s = x + y; + } + else error ("average_n: only one or two values supported"); + + return s / n; + } + variable ave1 = average_n (3.0, 1); % ==> 3.0 + variable ave2 = average_n (3.0, 5.0, 2); % ==> 4.0 + + + + Here, the last argument passed to average_n is an integer reflecting + the number of quantities to be averaged. Although this example works + fine, its principal limitation is obvious: it only supports one or two + values. Extending it to three or more values by adding more else if + constructs is rather straightforward but hardly worth the effort. + There must be a better way, and there is: + + + + define average_n (n) + { + variable s, x; + s = 0; + loop (n) + { + x = (); % get next value from stack + s += x; + } + return s / n; + } + + + + The principal limitation of this approach is that one must still pass + an integer that specifies how many values are to be averaged. + + Fortunately, a special variable exists that is local to every function + and contains the number of values that were passed to the function. + That variable has the name _NARGS and may be used as follows: + + + define average_n () + { + variable x, s = 0; + + if (_NARGS == 0) error ("Usage: ave = average_n (x, ...);"); + + loop (_NARGS) + { + x = (); + s += x; + } + return s / _NARGS; + } + + + + Here, if no arguments are passed to the function, a simple message + that indicates how it is to be used is printed out. + + + + 9.5. Returning Values + + + As stated earlier, the usual way to return values from a function is + via the return statement. This statement has the simple syntax + + return expression-list ; + + + where expression-list is a comma separated list of expressions. If + the function does not return any values, the expression list will be + empty. As an example of a function that can return multiple values, + consider + + + + define sum_and_diff (x, y) + { + variable sum, diff; + + sum = x + y; diff = x - y; + return sum, diff; + } + + + + which is a function returning two values. + + It is extremely important to note that the calling routine must + explicitly handle all values returned by a function. Although some + languages such as C do not have this restriction, S-Lang does and it + is a direct result of a S-Lang function's ability to return many + values and accept a variable number of parameters. Examples of + properly handling the above function include + + + variable s, d; + (s, d) = sum_and_diff (5, 4); % ignore neither + (s,) = sum_and_diff (5, 4); % ignore diff + (,) = sum_and_diff (5, 4); % ignore both sum and diff + + + + See the section below on assignment statements for more information + about this important point. + + + 9.6. Multiple Assignment Statement + + + + S-Lang functions can return more than one value, e.g., + + + define sum_and_diff (x, y) + { + return x + y, x - y; + } + + + + returns two values. It accomplishes this by placing both values on + the stack before returning. If you understand how S-Lang functions + handle a variable number of parameters (section ???), then it should + be rather obvious that one assigns such values to variables. One way + is to use, e.g., + + + sum_and_diff (9, 4); + d = (); + s = (); + + + + However, the most convenient way to accomplish this is to use a + multiple assignment statement such as + + + (s, d) = sum_and_diff (9, 4); + + + + The most general form of the multiple assignment statement is + + + ( var_1, var_2, ..., var_n ) = expression; + + + + In fact, internally the interpreter transforms this statement into the + form + + + expression; var_n = (); ... var_2 = (); var_1 = (); + + + + for further processing. + + If you do not care about one of return values, simply omit the + variable name from the list. For example, + + + (s, ) = sum_and_diff (9, 4); + + + + assigns the sum of 9 and 4 to s and the difference (9-4) will be + removed from the stack. + + As another example, the jed editor provides a function called down + that takes an integer argument and returns an integer. It is used to + move the current editing position down the number of lines specified + by the argument passed to it. It returns the number of lines it + successfully moved the editing position. Often one does not care + about the return value from this function. Although it is always + possible to handle the return value via + + + variable dummy = down (10); + + + + it is more convenient to use a multiple assignment expression and omit + the variable name, e.g., + + + () = down (10); + + + + Some functions return a variable number of values instead of a fixed + number. Usually, the value at the top of the stack will indicate the + actual number of return values. For such functions, the multiple + assignment statement cannot directly be used. To see how such + functions can be dealt with, consider the following function: + + + define read_line (fp) + { + variable line; + if (-1 == fgets (&line, fp)) + return -1; + return (line, 0); + } + + + + This function returns either one or two values, depending upon the + return value of fgets. Such a function may be handled as in the fol- + lowing example: + + + status = read_line (fp); + if (status != -1) + { + s = (); + . + . + } + + + + In this example, the last value returned by read_line is assigned to + status and then tested. If it is non-zero, the second return value is + assigned to s. In particular note the empty set of parenthesis in the + assignment to s. This simply indicates that whatever is on the top of + the stack when the statement is executed will be assigned to s. + + Before leaving this section it is important to reiterate the fact that + if a function returns a value, the caller must deal with that return + value. Otherwise, the value will continue to live onto the stack and + may eventually lead to a stack overflow error. Failing to handle the + return value of a function is the most common mistake that + inexperienced S-Lang programmers make. For example, the fflush + function returns a value that many C programmer's never check. + Instead of writing + + + fflush (fp); + + + + as one could in C, a S-Lang programmer should write + + + () = fflush (fp); + + + + in S-Lang. (Many good C programmer's write (void)fflush(fp) to indi- + cate that the return value is being ignored). + + + + 9.7. Exit-Blocks + + + + An exit-block is a set of statements that get executed when a + functions returns. They are very useful for cleaning up when a + function returns via an explicit call to return from deep within a + function. + + An exit-block is created by using the EXIT_BLOCK keyword according to + the syntax + + EXIT_BLOCK { statement-list } + + + where statement-list represents the list of statements that comprise + the exit-block. The following example illustrates the use of an exit- + block: + + + define simple_demo () + { + variable n = 0; + + EXIT_BLOCK { message ("Exit block called."); } + + forever + { + if (n == 10) return; + n++; + } + } + + + + Here, the function contains an exit-block and a forever loop. The + loop will terminate via the return statement when n is 10. Before it + returns, the exit-block will get executed. + + A function can contain multiple exit-blocks, but only the last one + encountered during execution will actually get executed. For example, + + + define simple_demo (n) + { + EXIT_BLOCK { return 1; } + + if (n != 1) + { + EXIT_BLOCK { return 2; } + } + return; + } + + + + If 1 is passed to this function, the first exit-block will get exe- + cuted because the second one would not have been encountered during + the execution. However, if some other value is passed, the second + exit-block would get executed. This example also illustrates that it + is possible to explicitly return from an exit-block, although nested + exit-blocks are illegal. + + + + 10. Name Spaces + + + + By default, all global variables and functions are defined in the + global namespace. In addition to the global namespace, every + compilation unit (e.g., a file containing S-Lang code) has an + anonymous namespace. Objects may be defined in the anonymous + namespace via the static declaration keyword. For example, + + + static variable x; + static define hello () { message ("hello"); } + + + + defines a variable x and a function hello in the anonymous namespace. + This is useful when one wants to define functions and variables that + are only to be used within the file, or more precisely the compilation + unit, that defines them. + + The implements function may be used to give the anonymous namespace a + name to allow access to its objects from outside the compilation unit + that defines them. For example, + + + implements ("foo"); + static variable x; + + + + allows the variable x to be accessed via foo->x, e.g., + + + if (foo->x == 1) foo->x = 2; + + + + The implements function does more than simply giving the anonymous + namespace a name. It also changes the default variable and function + declaration mode from public to static. That is, + + + implements ("foo"); + variable x; + + + + and + + + implements ("foo"); + static variable x; + + + + are equivalent. Then to create a public object within the namespace, + one must explicitly use the public keyword. + + Finally, the private keyword may be used to create an object that is + truly private within the compilation unit. For example, + implements ("foo"); + variable x; + private variable y; + + + + allows x to be accessed from outside the namespace via foo->x, however + y cannot be accessed. + + + + 11. Arrays + + + + An array is a container object that can contain many values of one + data type. Arrays are very useful objects and are indispensable for + certain types of programming. The purpose of this chapter is to + describe how arrays are defined and used in the S-Lang language. + + + 11.1. Creating Arrays + + + + The S-Lang language supports multi-dimensional arrays of all data + types. Since the Array_Type is a data type, one can even have arrays + of arrays. To create a multi-dimensional array of SomeType use the + syntax + + + SomeType [dim0, dim1, ..., dimN] + + + + Here dim0, dim1, ... dimN specify the size of the individual dimen- + sions of the array. The current implementation permits arrays consist + of up to 7 dimensions. When a numeric array is created, all its ele- + ments are initialized to zero. The initialization of other array + types depend upon the data type, e.g., String_Type and Struct_Type + arrays are initialized to NULL. + + As a concrete example, consider + + + a = Integer_Type [10]; + + + + which creates a one-dimensional array of 10 integers and assigns it to + a. Similarly, + + + b = Double_Type [10, 3]; + + + + creates a 30 element array of double precision numbers arranged in 10 + rows and 3 columns, and assigns it to b. + + + 11.1.1. Range Arrays + + + There is a more convenient syntax for creating and initializing a 1-d + arrays. For example, to create an array of ten integers whose + elements run from 1 through 10, one may simply use: + + + a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + + + Similarly, + + + b = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]; + + + + specifies an array of ten doubles. + + An even more compact way of specifying a numeric array is to use a + range-array. For example, + + + a = [0:9]; + + + + specifies an array of 10 integers whose elements range from 0 through + 9. The most general form of a range array is + + + [first-value : last-value : increment] + + + + where the increment is optional and defaults to 1. This creates an + array whose first element is first-value and whose successive values + differ by increment. last-value sets an upper limit upon the last + value of the array as described below. + + If the range array [a:b:c] is integer valued, then the interval + specified by a and b is closed. That is, the kth element of the array + x_k is given by x_k=a+ck and must satisfy a<=x_k<=b. Hence, the + number of elements in an integer range array is given by the + expression 1 + (b-a)/c. + + The situation is somewhat more complicated for floating point range + arrays. The interval specified by a floating point range array + [a:b:c] is semi-open such that b is not contained in the interval. In + particular, the kth element of [a:b:c] is given by x_k=a+kc such that + a<=x_k<b when c>=0, and b<x_k<=a otherwise. The number of elements in + the array is one greater than the largest k that satisfies the open + interval constraint. + + Here are a few examples that illustrate the above comments: + + + [1:5:1] ==> [1,2,3,4,5] + [1.0:5.0:1.0] ==> [1.0, 2.0, 3.0, 4.0] + [5:1:-1] ==> [5,4,3,2,1] + [5.0:1.0:-1.0] ==> [5.0, 4.0, 3.0, 2.0]; + [1:1] ==> [1] + [1.0:1.0] ==> [] + [1:-3] ==> [] + + + + 11.1.2. Creating arrays via the dereference operator + + + + Another way to create an array is apply the dereference operator @ to + the DataType_Type literal Array_Type. The actual syntax for this + operation resembles a function call + + variable a = @Array_Type (data-type, integer-array); + + + where data-type is of type DataType_Type and integer-array is a 1-d + array of integers that specify the size of each dimension. For exam- + ple, + + + variable a = @Array_Type (Double_Type, [10, 20]); + + + + will create a 10 by 20 array of doubles and assign it to a. This + method of creating arrays derives its power from the fact that it is + more flexible than the methods discussed in this section. We shall + encounter it again in section ??? in the context of the array_info + function. + + + + 11.2. Reshaping Arrays + + + It is sometimes possible to change the `shape' of an array using the + reshape function. For example, a 1-d 10 element array may be reshaped + into a 2-d array consisting of 5 rows and 2 columns. The only + restriction on the operation is that the arrays must be commensurate. + The reshape function follows the syntax + + reshape (array-name, integer-array); + + + where array-name specifies the array to be reshaped to have the dimen- + sions given by integer-array, a 1-dimensional array of integers. It + is important to note that this does not create a new array, it simply + reshapes the existing array. Thus, + + + variable a = Double_Type [100]; + reshape (a, [10, 10]); + + + + turns a into a 10 by 10 array. + + + + 11.3. Indexing Arrays + + + An individual element of an array may be referred to by its index. + For example, a[0] specifies the zeroth element of the one dimensional + array a, and b[3,2] specifies the element in the third row and second + column of the two dimensional array b. As in C array indices are + numbered from 0. Thus if a is a one-dimensional array of ten + integers, the last element of the array is given by a[9]. Using a[10] + would result in a range error. + + A negative index may be used to index from the end of the array, with + a[-1] referring to the last element of a, a[-2] referring to the next + to the last element, and so on. + + One may use the indexed value like any other variable. For example, + to set the third element of an integer array to 6, use + + + a[2] = 6; + + + + Similarly, that element may be used in an expression, such as + + + y = a[2] + 7; + + + + Unlike other S-Lang variables which inherit a type upon assignment, + array elements already have a type. For example, an attempt to assign + a string value to an element of an integer array will result in a + type-mismatch error. + + One may use any integer expression to index an array. A simple + example that computes the sum of the elements of 10 element 1-d array + is + + + variable i, s; + s = 0; + for (i = 0; i < 10; i++) s += a[i]; + + + + However, if the built-in sum function is available (not all programs + using S-Lang support this), then it should be used to compute the sum + of an array, e.g., + + + s = sum(a); + + + + Unlike many other languages, S-Lang permits arrays to be indexed by + other integer arrays. Suppose that a is a 1-d array of 10 doubles. + Now consider: + + + i = [6:8]; + b = a[i]; + + + + Here, i is a 1-dimensional range array of three integers with i[0] + equal to 6, i[1] equal to 7, and i[2] equal to 8. The statement b = + a[i]; will create a 1-d array of three doubles and assign it to b. + The zeroth element of b, b[0] will be set to the sixth element of a, + or a[6], and so on. In fact, these two simple statements are equiva- + lent to + + b = Double_Type [3]; + b[0] = a[6]; + b[1] = a[7]; + b[2] = a[8]; + + + + except that using an array of indices is not only much more conve- + nient, but executes much faster. + + More generally, one may use an index array to specify which elements + are to participate in a calculation. For example, consider + + + a = Double_Type [1000]; + i = [0:499]; + j = [500:999]; + a[i] = -1.0; + a[j] = 1.0; + + + + This creates an array of 1000 doubles and sets the first 500 elements + to -1.0 and the last 500 to 1.0. Actually, one may do away with the i + and j variables altogether and use + + + a = Double_Type [1000]; + a [[0:499]] = -1.0; + a [[500:999]] = 1.0; + + + + It is important to understand the syntax used and, in particular, to + note that a[[0:499]] is not the same as a[0:499]. In fact, the latter + will generate a syntax error. + + Often, it is convenient to use a rubber range to specify indices. For + example, a[[500:]] specifies all elements of a whose index is greater + than or equal to 500. Similarly, a[[:499]] specifies the first 500 + elements of a. Finally, a[[:]] specifies all the elements of a; + however, using a[*] is more convenient. + + One should be careful when using index arrays with negative elements. + As pointed out above, a negative index is used to index from the end + of the array. That is, a[-1] refers to the last element of a. How + should a[[[0:-1]] be interpreted? By itself, [0:-1] is an empty + array; hence, one might expect a[[0:-1]] to refer to no elements. + However, when used in an array indexing context, [0:-1] is interpreted + as an array indexing the first through the last elements of the array. + While this is a very convenient mechanism to specifiy the last 3 + elements of an array using a[[-3:-1]], it is very easy to forget these + semantics. + + Now consider a multi-dimensional array. For simplicity, suppose that + a is a 100 by 100 array of doubles. Then the expression a[0, *] + specifies all elements in the zeroth row. Similarly, a[*, 7] + specifies all elements in the seventh column. Finally, a[[3:5][6:12]] + specifies the 3 by 7 region consisting of rows 3, 4, and 5, and + columns 6 through 12 of a. + + We conclude this section with a few examples. + + Here is a function that computes the trace (sum of the diagonal + elements) of a square 2 dimensional n by n array: + + + define array_trace (a, n) + { + variable s = 0, i; + for (i = 0; i < n; i++) s += a[i, i]; + return s; + } + + + + This fragment creates a 10 by 10 integer array, sets its diagonal ele- + ments to 5, and then computes the trace of the array: + + + a = Integer_Type [10, 10]; + for (j = 0; j < 10; j++) a[j, j] = 5; + the_trace = array_trace(a, 10); + + + + We can get rid of the for loop as follows: + + + j = Integer_Type [10, 2]; + j[*,0] = [0:9]; + j[*,1] = [0:9]; + a[j] = 5; + + + + Here, the goal was to construct a 2-d array of indices that correspond + to the diagonal elements of a, and then use that array to index a. To + understand how this works, consider the middle statements. They are + equivalent to the following for loops: + + + variable i; + for (i = 0; i < 10; i++) j[i, 0] = i; + for (i = 0; i < 10; i++) j[i, 1] = i; + + + + Thus, row n of j will have the value (n,n), which is precisely what + was sought. + + Another example of this technique is the function: + + + define unit_matrix (n) + { + variable a = Integer_Type [n, n]; + variable j = Integer_Type [n, 2]; + j[*,0] = [0:n - 1]; + j[*,1] = [0:n - 1]; + + a[j] = 1; + return a; + } + + This function creates an n by n unit matrix, that is a 2-d n by n + array whose elements are all zero except on the diagonal where they + have a value of 1. + + + + 11.4. Arrays and Variables + + + When an array is created and assigned to a variable, the interpreter + allocates the proper amount of space for the array, initializes it, + and then assigns to the variable a reference to the array. So, a + variable that represents an array has a value that is really a + reference to the array. This has several consequences, some good and + some bad. It is believed that the advantages of this representation + outweigh the disadvantages. First, we shall look at the positive + aspects. + + When a variable is passed to a function, it is always the value of the + variable that gets passed. Since the value of a variable representing + an array is a reference, a reference to the array gets passed. One + major advantage of this is rather obvious: it is a fast and efficient + way to pass the array. This also has another consequence that is + illustrated by the function + + + define init_array (a, n) + { + variable i; + + for (i = 0; i < n; i++) a[i] = some_function (i); + } + + + + where some_function is a function that generates a scalar value to + initialize the ith element. This function can be used in the follow- + ing way: + + + variable X = Double_Type [100000]; + init_array (X, 100000); + + + + Since the array is passed to the function by reference, there is no + need to make a separate copy of the 100000 element array. As pointed + out above, this saves both execution time and memory. The other + salient feature to note is that any changes made to the elements of + the array within the function will be manifested in the array outside + the function. Of course, in this case, this is a desirable side- + effect. + + To see the downside of this representation, consider: + + + variable a, b; + a = Double_Type [10]; + b = a; + a[0] = 7; + + + What will be the value of b[0]? Since the value of a is really a ref- + erence to the array of ten doubles, and that reference was assigned to + b, b also refers to the same array. Thus any changes made to the ele- + ments of a, will also be made implicitly to b. + + This begs the question: If the assignment of one variable which + represents an array, to another variable results in the assignment of + a reference to the array, then how does one make separate copies of + the array? There are several answers including using an index array, + e.g., b = a[*]; however, the most natural method is to use the + dereference operator: + + + variable a, b; + a = Double_Type [10]; + b = @a; + a[0] = 7; + + + + In this example, a separate copy of a will be created and assigned to + b. It is very important to note that S-Lang never implicitly derefer- + ences an object. So, one must explicitly use the dereference opera- + tor. This means that the elements of a dereferenced array are not + themselves dereferenced. For example, consider dereferencing an array + of arrays, e.g., + + + variable a, b; + a = Array_Type [2]; + a[0] = Double_Type [10]; + a[1] = Double_Type [10]; + b = @a; + + + + In this example, b[0] will be a reference to the array that a[0] ref- + erences because a[0] was not explicitly dereferenced. + + + 11.5. Using Arrays in Computations + + + + Many functions and operations work transparently with arrays. For + example, if a and b are arrays, then the sum a + b is an array whose + elements are formed from the sum of the corresponding elements of a + and b. A similar statement holds for all other binary and unary + operations. + + Let's consider a simple example. Suppose, that we wish to solve a set + of n quadratic equations whose coefficients are given by the 1-d + arrays a, b, and c. In general, the solution of a quadratic equation + will be two complex numbers. For simplicity, suppose that all we + really want is to know what subset of the coefficients, a, b, c, + correspond to real-valued solutions. In terms of for loops, we can + write: + + + + variable i, d, index_array; + index_array = Integer_Type [n]; + for (i = 0; i < n; i++) + { + d = b[i]^2 - 4 * a[i] * c[i]; + index_array [i] = (d >= 0.0); + } + + + + In this example, the array index_array will contain a non-zero value + if the corresponding set of coefficients has a real-valued solution. + This code may be written much more compactly and with more clarity as + follows: + + + variable index_array = ((b^2 - 4 * a * c) >= 0.0); + + + + S-Lang has a powerful built-in function called where. This function + takes an array of integers and returns a 2-d array of indices that + correspond to where the elements of the input array are non-zero. + This simple operation is extremely useful. For example, suppose a is a + 1-d array of n doubles, and it is desired to set to zero all elements + of the array whose value is less than zero. One way is to use a for + loop: + + + for (i = 0; i < n; i++) + if (a[i] < 0.0) a[i] = 0.0; + + + + If n is a large number, this statement can take some time to execute. + The optimal way to achieve the same result is to use the where func- + tion: + + + a[where (a < 0.0)] = 0; + + + + Here, the expression (a < 0.0) returns an array whose dimensions are + the same size as a but whose elements are either 1 or 0, according to + whether or not the corresponding element of a is less than zero. This + array of zeros and ones is then passed to where which returns a 2-d + integer array of indices that indicate where the elements of a are + less than zero. Finally, those elements of a are set to zero. + + As a final example, consider once more the example involving the set + of n quadratic equations presented above. Suppose that we wish to get + rid of the coefficients of the previous example that generated non- + real solutions. Using an explicit for loop requires code such as: + + + + variable i, j, nn, tmp_a, tmp_b, tmp_c; + + nn = 0; + for (i = 0; i < n; i++) + if (index_array [i]) nn++; + + tmp_a = Double_Type [nn]; + tmp_b = Double_Type [nn]; + tmp_c = Double_Type [nn]; + + j = 0; + for (i = 0; i < n; i++) + { + if (index_array [i]) + { + tmp_a [j] = a[i]; + tmp_b [j] = b[i]; + tmp_c [j] = c[i]; + j++; + } + } + a = tmp_a; + b = tmp_b; + c = tmp_c; + + + + Not only is this a lot of code, it is also clumsy and error-prone. + Using the where function, this task is trivial: + + + variable i; + i = where (index_array != 0); + a = a[i]; + b = b[i]; + c = c[i]; + + + + All the examples up to now assumed that the dimensions of the array + were known. Although the intrinsic function length may be used to get + the total number of elements of an array, it cannot be used to get the + individual dimensions of a multi-dimensional array. However, the + function array_info may be used to get information about an array, + such as its data type and size. The function returns three values: + the data type, the number of dimensions, and an integer array + containing the size of each dimension. It may be used to determine + the number of rows of an array as follows: + + + define num_rows (a) + { + variable dims, type, num_dims; + + (dims, num_dims, type) = array_info (a); + return dims[0]; + } + + + + The number of columns may be obtained in a similar manner: + + + define num_cols (a) + { + variable dims, type, num_dims; + + (dims, num_dims, type) = array_info (a); + if (num_dims > 1) return dims[1]; + return 1; + } + + + + Another use of array_info is to create an array that has the same + number of dimensions as another array: + + + define make_int_array (a) + { + variable dims, num_dims, type; + + (dims, num_dims, type) = array_info (a); + return @Array_Type (Integer_Type, dims); + } + + + + 12. Associative Arrays + + + + An associative array differs from an ordinary array in the sense that + its size is not fixed and that is indexed by a string, called the key. + For example, consider: + + + variable A = Assoc_Type [Integer_Type]; + A["alpha"] = 1; + A["beta"] = 2; + A["gamma"] = 3; + + + + Here, A represents an associative array of integers (Integer_Type) and + three keys have been added to the array. + + As the example suggests, an associative array may be created using one + of the following forms: + + Assoc_Type [type] Assoc_Type [type, default-value] Assoc_Type [] + + + The last form returns an associative array of Any_Type objects allow- + ing any type of object to may be stored in the array. + + The form involving a default-value is useful for associating a default + value for non-existent array members. This feature is explained in + more detail below. + + There are several functions that are specially designed to work with + associative arrays. These include: + + o assoc_get_keys, which returns an ordinary array of strings + containing the keys in the array. + + + o assoc_get_values, which returns an ordinary array of the values of + the associative array. + + + o assoc_key_exists, which can be used to determine whether or not a + key exists in the array. + + o assoc_delete_key, which may be used to remove a key (and its value) + from the array. + + To illustrate the use of an associative array, consider the problem of + counting the number of repeated occurrences of words in a list. Let + the word list be represented as an array of strings given by + word_list. The number of occurrences of each word may be stored in an + associative array as follows: + + + + variable a, word; + a = Assoc_Type [Integer_Type]; + foreach (word_list) + { + word = (); + if (0 == assoc_key_exists (a, word)) + a[word] = 0; + a[word]++; % same as a[word] = a[word] + 1; + } + + + + Note that assoc_key_exists was necessary to determine whether or not a + word was already added to the array in order to properly initialize + it. However, by creating the associative array with a default value + of 0, the above code may be simplified to + + + variable a, word; + a = Assoc_Type [Integer_Type, 0]; + foreach (word_list) + { + word = (); + a[word]++; + } + + + + 13. Structures and User-Defined Types + + + + A structure is a heterogeneous container object, i.e., it is an object + with elements whose values do not have to be of the same data type. + The elements or fields of a structure are named, and one accesses a + particular field of the structure via the field name. This should be + contrasted with an array whose values are of the same type, and whose + elements are accessed via array indices. + + A user-defined data type is a structure with a fixed set of fields + defined by the user. + + + 13.1. Defining a Structure + + + The struct keyword is used to define a structure. The syntax for this + operation is: + + struct {field-name-1, field-name-2, ... field-name-N}; + + + This creates and returns a structure with N fields whose names are + specified by field-name-1, field-name-2, ..., field-name-N. When a + structure is created, all its fields are initialized to NULL. + + For example, + + + variable t = struct { city_name, population, next }; + + + + creates a structure with three fields and assigns it to the variable + t. + + Alternatively, a structure may be created by dereferencing + Struct_Type. For example, the above structure may also be created + using one of the two forms: + + + t = @Struct_Type ("city_name", "population", "next"); + t = @Struct_Type (["city_name", "population", "next"]); + + + + These are useful when creating structures dynamically where one does + not know the name of the fields until run-time. + + Like arrays, structures are passed around via a references. Thus, in + the above example, the value of t is a reference to the structure. + This means that after execution of + + + variable u = t; + + + + both t and u refer to the same structure, since only the reference was + used in the assignment. To actually create a new copy of the struc- + ture, use the dereference operator, e.g., + variable u = @t; + + + + 13.2. Accessing the Fields of a Structure + + + The dot (.) operator is used to specify the particular field of + structure. If s is a structure and field_name is a field of the + structure, then s.field_name specifies that field of s. This + specification can be used in expressions just like ordinary variables. + Again, consider + + + variable t = struct { city_name, population, next }; + + + + described in the last section. Then, + + + t.city_name = "New York"; + t.population = 13000000; + if (t.population > 200) t = t.next; + + + + are all valid statements involving the fields of t. + + + 13.3. Linked Lists + + + One of the most important uses of structures is to create a dynamic + data structure such as a linked-list. A linked-list is simply a chain + of structures that are linked together such that one structure in the + chain is the value of a field of the previous structure in the chain. + To be concrete, consider the structure discussed earlier: + + + variable t = struct { city_name, population, next }; + + + + and suppose that we desire to create a list of such structures. The + purpose of the next field is to provide the link to the next structure + in the chain. Suppose that there exists a function, read_next_city, + that reads city names and populations from a file. Then we can create + the list via: + + + + define create_population_list () + { + variable city_name, population, list_root, list_tail; + variable next; + + list_root = NULL; + while (read_next_city (&city_name, &population)) + { + next = struct {city_name, population, next }; + + next.city_name = city_name; + next.population = population; + next.next = NULL; + + if (list_root == NULL) + list_root = next; + else + list_tail.next = next; + + list_tail = next; + } + return list_root; + } + + + + In this function, the variables list_root and list_tail represent the + beginning and end of the list, respectively. As long as read_next_city + returns a non-zero value, a new structure is created, initialized, and + then appended to the list via the next field of the list_tail struc- + ture. On the first time through the loop, the list is created via the + assignment to the list_root variable. + + This function may be used as follows: + + + variable Population_List = create_population_list (); + if (Population_List == NULL) error ("List is empty"); + + + + We can create other functions that manipulate the list. An example is + a function that finds the city with the largest population: + + + define get_largest_city (list) + { + variable largest; + + largest = list; + while (list != NULL) + { + if (list.population > largest.population) + largest = list; + list = list.next; + } + return largest.city_name; + } + + vmessage ("%s is the largest city in the list", + get_largest_city (Population_List))); + + + + The get_largest_city is a typical example of how one traverses a lin- + ear linked-list by starting at the head of the list and successively + moves to the next element of the list via the next field. + + In the previous example, a while loop was used to traverse the linked + list. It is faster to use a foreach loop for this: + + + define get_largest_city (list) + { + variable largest, elem; + + largest = list; + foreach (list) + { + elem = (); + if (item.population > largest.population) + largest = item; + } + return largest.city_name; + } + + + + Here a foreach loop has been used to walk the list via its next field. + If the field name was not next, then it would have been necessary to + use the using form of the foreach statement. For example, if the + field name implementing the linked list was next_item, then + + + foreach (list) using ("next_item") + { + elem = (); + . + . + } + + + + would have been used. In other words, unless otherwise indicated via + the using clause, foreach walks the list using a field named next. + + Now consider a function that sorts the list according to population. + To illustrate the technique, a bubble-sort will be used, not because + it is efficient, it is not, but because it is simple and intuitive. + + + + define sort_population_list (list) + { + variable changed; + variable node, next_node, last_node; + do + { + changed = 0; + node = list; + next_node = node.next; + last_node = NULL; + while (next_node != NULL) + { + if (node.population < next_node.population) + { + % swap node and next_node + node.next = next_node.next; + next_node.next = node; + if (last_node != NULL) + last_node.next = next_node; + + if (list == node) list = next_node; + node = next_node; + next_node = node.next; + changed++; + } + last_node = node; + node = next_node; + next_node = next_node.next; + } + } + while (changed); + + return list; + } + + + + Note the test for equality between list and node, i.e., + + + if (list == node) list = next_node; + + + + It is important to appreciate the fact that the values of these vari- + ables are references to structures, and that the comparison only com- + pares the references and not the actual structures they reference. If + it were not for this, the algorithm would fail. + + + 13.4. Defining New Types + + + A user-defined data type may be defined using the typedef keyword. In + the current implementation, a user-defined data type is essentially a + structure with a user-defined set of fields. For example, in the + previous section a structure was used to represent a city/population + pair. We can define a data type called Population_Type to represent + the same information: + + + + typedef struct + { + city_name, + population + } Population_Type; + + + + This data type can be used like all other data types. For example, an + array of Population_Type types can be created, + + + variable a = Population_Type[10]; + + + + and `populated' via expressions such as + + + a[0].city_name = "Boston"; + a[0].population = 2500000; + + + + The new type Population_Type may also be used with the typeof func- + tion: + + + if (Population_Type = typeof (a)) city = a.city_name; + + + + The dereference @ may be used to create an instance of the new type: + + + a = @Population_Type; + a.city_name = "Calcutta"; + a.population = 13000000; + + + + 14. Error Handling + + + + Many intrinsic functions signal errors in the event of failure. User + defined functions may also generate an error condition via the error + function. Depending upon the severity of the error, it can be caught + and cleared using a construct called an error-block. + + + 14.1. Error-Blocks + + + When the interpreter encounters a recoverable run-time error, it will + return to top-level by unwinding its function call stack. Any error- + blocks that it encounters as part of this unwinding process will get + executed. Errors such as syntax errors and memory allocation errors + are not recoverable, and error-blocks will not get executed when such + errors are encountered. + + An error-block is defined using the syntax + + + ERROR_BLOCK { statement-list } + + + + where statement-list represents a list of statements that comprise the + error-block. A simple example of an error-block is + + + define simple (a) + { + ERROR_BLOCK { message ("error-block executed"); } + if (a) error ("Triggering Error"); + message ("hello"); + } + + + + Executing this function via simple(0) will result in the message + "hello". However, calling it using simple(1) will generate an error + that will be caught, but not cleared, by the error-block and the + "error-block executed" message will result. + + Error-blocks are never executed unless triggered by an error. The + only exception to this is when the user explicitly indicates that the + error-block in scope should execute. This is indicated by the special + keyword EXECUTE_ERROR_BLOCK. For example, simple could be recoded as + + + define simple (a) + { + variable err_string = "error-block executed"; + ERROR_BLOCK { message (err_string); } + if (a) error ("Triggering Error"); + err_string = "hello"; + EXECUTE_ERROR_BLOCK; + } + + + + Please note that EXECUTE_ERROR_BLOCK does not initiate an error + condition; it simply causes the error-block to be executed and control + will pass onto the next statement following the EXECUTE_ERROR_BLOCK + statement. + + + 14.2. Clearing Errors + + + Once an error has been caught by an error-block, the error can be + cleared by the _clear_error function. After the error has been + cleared, execution will resume at the next statement at the level of + the error block following the statement that generated the error. For + example, consider: + + + define make_error () + { + error ("Error condition created."); + message ("This statement is not executed."); + } + + define test () + { + ERROR_BLOCK + { + _clear_error (); + } + make_error (); + message ("error cleared."); + } + + + + Calling test will trigger an error in the make_error function, but + will get cleared in the test function. The call-stack will unwind + from make_error back into test where the error-block will get exe- + cuted. As a result, execution resumes after the statement that makes + the call to make_error since this statement is at the same level as + the error-block that cleared the error. + + Here is another example that illustrates how multiple error-blocks + work: + + + + define example () + { + variable n = 0, s = ""; + variable str; + + ERROR_BLOCK { + str = sprintf ("s=%s,n=%d", s, n); + _clear_error (); + } + + forever + { + ERROR_BLOCK { + s += "0"; + _clear_error (); + } + + if (n == 0) error (""); + + ERROR_BLOCK { + s += "1"; + } + + if (n == 1) error (""); + n++; + } + return str; + } + + + + Here, three error-blocks have been declared. One has been declared + outside the forever loop and the other two have been declared inside + the forever loop. Each time through the loop, the variable n is + incremented and a different error-block is triggered. The error-block + that gets triggered is the last one encountered, since that will be + the one in scope. On the first time through the loop, n will be zero + and the first error-block in the loop will get executed. This error + block clears the error and execution resumes following the if state- + ment that triggered the error. The variable n will get incremented to + 1 and, on the second cycle through the loop the second if statement + will trigger an error causing the second error-block to execute. This + time, the error is not cleared and the call-stack unwinds out of the + forever loop, at which point the error-block outside the loop is in + scope, causing it to execute. This error-block prints out the values + of the variables s and n. It will clear the error and execution + resumes on the statement following the forever loop. The result of + this complicated series of events is that the function will return the + string "s=01,n=1". + + + + 15. Loading Files: evalfile and autoload + + + + 16. File Input/Output + + + + S-Lang provides built-in supports for two different I/O facilities. + The simplest interface is modeled upon the C language stdio streams + interface and consists of functions such as fopen, fgets, etc. The + other interface is modeled on a lower level POSIX interface consisting + of functions such as open, read, etc. In addition to permitting more + control, the lower level interface permits one to access network + objects as well as disk files. + + + 16.1. Input/Output via stdio + + + 16.1.1. Stdio Overview + + The stdio interface consists of the following functions: + + o fopen, which opens a file for read or writing. + + o fclose, which closes a file opened by fopen. + + o fgets, used to read a line from the file. + + o fputs, which writes text to the file. + + o fprintf, used to write formatted text to the file. + + o fwrite, which may be used to write objects to the file. + + o fread, which reads a specified number of objects from the file. + + o feof, which is used to test whether the file pointer is at the end + of the file. + + o ferror, which is used to see whether or not the stream associated + with the file has an error. + + + o clearerr, which clears the end-of-file and error indicators for the + stream. + + o fflush, used to force all buffered data associated with the stream + to be written out. + + o ftell, which is used to query the file position indicator of the + stream. + + + o fseek, which is used to set the position of the file position + indicator of the stream. + + o fgetslines, which reads all the lines in a text file and returns + them as an array of strings. + + In addition, the interface supports the popen and pclose functions on + systems where the corresponding C functions are available. + + Before reading or writing to a file, it must first be opened using the + fopen function. The only exceptions to this rule involves use of the + pre-opened streams: stdin, stdout, and stderr. fopen accepts two + arguments: a file name and a string argument that indicates how the + file is to be opened, e.g., for reading, writing, update, etc. It + returns a File_Type stream object that is used as an argument to all + other functions of the stdio interface. Upon failure, it returns + NULL. See the reference manual for more information about fopen. + + + 16.1.2. Stdio Examples + + + In this section, some simple examples of the use of the stdio + interface is presented. It is important to realize that all the + functions of the interface return something, and that return value + must be dealt with. + + The first example involves writing a function to count the number of + lines in a text file. To do this, we shall read in the lines, one by + one, and count them: + + + define count_lines_in_file (file) + { + variable fp, line, count; + + fp = fopen (file, "r"); % Open the file for reading + if (fp == NULL) + verror ("%s failed to open", file); + + count = 0; + while (-1 != fgets (&line, fp)) + count++; + + () = fclose (fp); + return count; + } + + + + Note that &line was passed to the fgets function. When fgets returns, + line will contain the line of text read in from the file. Also note + how the return value from fclose was handled. + + Although the preceding example closed the file via fclose, there is no + need to explicitly close a file because S-Lang will automatically + close the file when it is no longer referenced. Since the only + variable to reference the file is fp, it would have automatically been + closed when the function returned. + + Suppose that it is desired to count the number of characters in the + file instead of the number of lines. To do this, the while loop could + be modified to count the characters as follows: + + + while (-1 != fgets (&line, fp)) + count += strlen (line); + + + + The main difficulty with this approach is that it will not work for + binary files, i.e., files that contain null characters. For such + files, the file should be opened in binary mode via + + + fp = fopen (file, "rb"); + + + + and then the data read in using the fread function: + + + while (-1 != fread (&line, Char_Type, 1024, fp)) + count += bstrlen (line); + + + + The fread function requires two additional arguments: the type of + object to read (Char_Type in the case), and the number of such objects + to read. The function returns the number of objects actually read, or + -1 upon failure. The bstrlen function was used to compute the length + of line because for Char_Type or UChar_Type objects, the fread func- + tion assigns a binary string (BString_Type) to line. + + The foreach construct also works with File_Type objects. For example, + the number of characters in a file may be counted via + + + foreach (fp) using ("char") + { + ch = (); + count++; + } + + + + To count the number of lines, one can use: + + + foreach (fp) using ("line") + { + line = (); + num_lines++; + count += strlen (line); + } + + + + Often one is not interested in trailing whitespace in the lines of a + file. To have trailing whitespace automatically stripped from the + lines as they are read in, use the "wsline" form, e.g., + + + foreach (fp) using ("wsline") + { + line = (); + . + . + } + + + + Finally, it should be mentioned that none of these examples should be + used to count the number of bytes in a file when that information is + more readily accessible by another means. For example, it is + preferable to get this information via the stat_file function: + + + + define count_chars_in_file (file) + { + variable st; + + st = stat_file (file); + if (st == NULL) + error ("stat_file failed."); + return st.st_size; + } + + + + 16.2. POSIX I/O + + + + 16.3. Advanced I/O techniques + + + The previous examples illustrate how to read and write objects of a + single data-type from a file, e.g., + + + num = fread (&a, Double_Type, 20, fp); + + + + would result in a Double_Type[num] array being assigned to a if suc- + cessful. However, suppose that the binary data file consists of num- + bers in a specified byte-order. How can one read such objects with + the proper byte swapping? The answer is to use the fread function to + read the objects as Char_Type and then unpack the resulting string + into the specified data type, or types. This process is facilitated + using the pack and unpack functions. + + The pack function follows the syntax + + BString_Type pack (format-string, item-list); + + + and combines the objects in the item-list according to format-string + into a binary string and returns the result. Likewise, the unpack + function may be used to convert a binary string into separate data + objects: + + (variable-list) = unpack (format-string, binary-string); + + + The format string consists of one or more data-type specification + characters, and each may be followed by an optional decimal length + specifier. Specifically, the data-types are specified according to the + following table: + + + + c char + C unsigned char + h short + H unsigned short + i int + I unsigned int + l long + L unsigned long + j 16 bit int + J 16 unsigned int + k 32 bit int + K 32 bit unsigned int + f float + d double + F 32 bit float + D 64 bit float + s character string, null padded + S character string, space padded + x a null pad character + + + + A decimal length specifier may follow the data-type specifier. With + the exception of the s and S specifiers, the length specifier indi- + cates how many objects of that data type are to be packed or unpacked + from the string. When used with the s or S specifiers, it indicates + the field width to be used. If the length specifier is not present, + the length defaults to one. + + With the exception of c, C, s, S, and x, each of these may be prefixed + by a character that indicates the byte-order of the object: + + + > big-endian order (network order) + < little-endian order + = native byte-order + + + + The default is native byte order. + + Here are a few examples that should make this more clear: + + + a = pack ("cc", 'A', 'B'); % ==> a = "AB"; + a = pack ("c2", 'A', 'B'); % ==> a = "AB"; + a = pack ("xxcxxc", 'A', 'B'); % ==> a = "\0\0A\0\0B"; + a = pack ("h2", 'A', 'B'); % ==> a = "\0A\0B" or "\0B\0A" + a = pack (">h2", 'A', 'B'); % ==> a = "\0\xA\0\xB" + a = pack ("<h2", 'A', 'B'); % ==> a = "\0B\0A" + a = pack ("s4", "AB", "CD"); % ==> a = "AB\0\0" + a = pack ("s4s2", "AB", "CD"); % ==> a = "AB\0\0CD" + a = pack ("S4", "AB", "CD"); % ==> a = "AB " + a = pack ("S4S2", "AB", "CD"); % ==> a = "AB CD" + + + + When unpacking, if the length specifier is greater than one, then an + array of that length will be returned. In addition, trailing + whitespace and null character are stripped when unpacking an object + given by the S specifier. Here are a few examples: + + + (x,y) = unpack ("cc", "AB"); % ==> x = 'A', y = 'B' + x = unpack ("c2", "AB"); % ==> x = ['A', 'B'] + x = unpack ("x<H", "\0\xAB\xCD"); % ==> x = 0xCDABuh + x = unpack ("xxs4", "a b c\0d e f"); % ==> x = "b c\0" + x = unpack ("xxS4", "a b c\0d e f"); % ==> x = "b c" + + + + 16.3.1. Example: Reading /var/log/wtmp + + + Consider the task of reading the Unix system file /var/log/utmp, which + contains login records about who logged onto the system. This file + format is documented in section 5 of the online Unix man pages, and + consists of a sequence of entries formatted according to the C + structure utmp defined in the utmp.h C header file. The actual + details of the structure may vary from one version of Unix to the + other. For the purposes of this example, consider its definition + under the Linux operating system running on an Intel processor: + + + struct utmp { + short ut_type; /* type of login */ + pid_t ut_pid; /* pid of process */ + char ut_line[12]; /* device name of tty - "/dev/" */ + char ut_id[2]; /* init id or abbrev. ttyname */ + time_t ut_time; /* login time */ + char ut_user[8]; /* user name */ + char ut_host[16]; /* host name for remote login */ + long ut_addr; /* IP addr of remote host */ + }; + + + + On this system, pid_t is defined to be an int and time_t is a long. + Hence, a format specifier for the pack and unpack functions is easily + constructed to be: + + + "h i S12 S2 l S8 S16 l" + + + + However, this particular definition is naive because it does not allow + for structure padding performed by the C compiler in order to align + the data types on suitable word boundaries. Fortunately, the intrin- + sic function pad_pack_format may be used to modify a format by adding + the correct amount of padding in the right places. In fact, + pad_pack_format applied to the above format on an Intel-based Linux + system produces the result: + + + "h x2 i S12 S2 x2 l S8 S16 l" + + + + Here we see that 4 bytes of padding were added. + + The other missing piece of information is the size of the structure. + This is useful because we would like to read in one structure at a + time using the fread function. Knowing the size of the various data + types makes this easy; however it is even easier to use the + sizeof_pack intrinsic function, which returns the size (in bytes) of + the structure described by the pack format. + + So, with all the pieces in place, it is rather straightforward to + write the code: + + + variable format, size, fp, buf; + + typedef struct + { + ut_type, ut_pid, ut_line, ut_id, + ut_time, ut_user, ut_host, ut_addr + } UTMP_Type; + + format = pad_pack_format ("h i S12 S2 l S8 S16 l"); + size = sizeof_pack (format); + + define print_utmp (u) + { + + () = fprintf (stdout, "%-16s %-12s %-16s %s\n", + u.ut_user, u.ut_line, u.ut_host, ctime (u.ut_time)); + } + + + fp = fopen ("/var/log/utmp", "rb"); + if (fp == NULL) + error ("Unable to open utmp file"); + + () = fprintf (stdout, "%-16s %-12s %-16s %s\n", + "USER", "TTY", "FROM", "LOGIN@"); + + variable U = @UTMP_Type; + + while (-1 != fread (&buf, Char_Type, size, fp)) + { + set_struct_fields (U, unpack (format, buf)); + print_utmp (U); + } + + () = fclose (fp); + + + + A few comments about this example are in order. First of all, note + that a new data type called UTMP_Type was created, although this was + not really necessary. We also opened the file in binary mode, but + this too is optional under a Unix system where there is no distinction + between binary and text modes. The print_utmp function does not print + all of the structure fields. Finally, last but not least, the return + values from fprintf and fclose were dealt with. + + + + 17. Debugging + + + + The current implementation provides no support for an interactive + debugger, although a future version will. Nevertheless, S-Lang has + several features that aid the programmer in tracking down problems, + including function call tracebacks and the tracing of function calls. + However, the biggest debugging aid stems from the fact that the + language is interpreted permitting one to easily add debugging + statements to the code. + + To enable debugging information, add the lines + + + _debug_info = 1; + _traceback = 1; + + + + to the top of the source file of the code containing the bug and the + reload the file. Setting the _debug_info variable to 1 causes line + number information to be compiled into the functions when the file is + loaded. The _traceback variable controls whether or not traceback + information should be generated. If it is set to 1, the values of + local variables will be dumped when the traceback is generated. Set- + ting this variable to -1 will cause only function names to be reported + in the traceback. + + Here is an example of a traceback report: + + + S-Lang Traceback: error + S-Lang Traceback: verror + S-Lang Traceback: (Error occurred on line 65) + S-Lang Traceback: search_generic_search + Local Variables: + $0: Type: String_Type, Value: "Search forward:" + $1: Type: Integer_Type, Value: 1 + $2: Type: Ref_Type, Value: _function_return_1 + $3: Type: String_Type, Value: "abcdefg" + $4: Type: Integer_Type, Value: 1 + S-Lang Traceback: (Error occurred on line 72) + S-Lang Traceback: search_forward + + + + There are several ways to read this report; perhaps the simplest is to + read it from the bottom. This report says that on line 72, the + search_forward function called the search_generic_search function. On + line 65 it called the verror function, which called error. The + search_generic_search function contains 5 local variables and are rep- + resented symbolically as $0 through $4. + + + + 18. Regular Expressions + + + The S-Lang library includes a regular expression (RE) package that may + be used by an application embedding the library. The RE syntax should + be familiar to anyone acquainted with regular expressions. In this + section the syntax of the S-Lang regular expressions is discussed. + + + 18.1. S-Lang RE Syntax + + + A regular expression specifies a pattern to be matched against a + string, and has the property that the contcatenation of two REs is + also a RE. + + The S-Lang library supports the following standard regular + expressions: + + + . match any character except newline + * matches zero or more occurences of previous RE + + matches one or more occurences of previous RE + ? matches zero or one occurence of previous RE + ^ matches beginning of a line + $ matches end of line + [ ... ] matches any single character between brackets. + For example, [-02468] matches `-' or any even digit. + and [-0-9a-z] matches `-' and any digit between 0 and 9 + as well as letters a through z. + \< Match the beginning of a word. + \> Match the end of a word. + \( ... \) + \1, \2, ..., \9 Matches the match specified by nth \( ... \) + expression. + + + + In addition the following extensions are also supported: + + + \c turn on case-sensitivity (default) + \C turn off case-sensitivity + \d match any digit + \e match ESC char + + + + Here are some simple examples: + + "^int " matches the "int " at the beginning of a line. + + "\<money\>" matches "money" but only if it appears as a separate word. + + "^$" matches an empty line. + + A more complex pattern is + + + "\(\<[a-zA-Z]+\>\)[ ]+\1\>" + + + + which matches any word repeated consecutively. Note how the grouping + operators \( and \) are used to define the text matched by the + enclosed regular expression, and then subsequently referred to \1. + + Finally, remember that when used in string literals either in the S- + Lang language or in the C language, care must be taken to "double-up" + the '\' character since both languages treat it as an escape + character. + + + 18.2. Differences between S-Lang and egrep REs + + + There are several differences between S-Lang regular expressions and, + e.g., egrep regular expressions. + + The most notable difference is that the S-Lang regular expressions do + not support the OR operator | in expressions. This means that "a|b" + or "a\|b" do not have the meaning that they have in regular expression + packages that support egrep-style expressions. + + The other main difference is that while S-Lang regular expressions + support the grouping operators \( and \), they are only used as a + means of specifying the text that is matched. That is, the expression + + + "@\([a-z]*\)@.*@\1@" + + + + matches "xxx@abc@silly@abc@yyy", where the pattern \1 matches the text + enclosed by the \( and \) expressions. However, in the current imple- + mentation, the grouping operators are not used to group regular + expressions to form a single regular expression. Thus expression such + as "\(hello\)*" is not a pattern to match zero or more occurances of + "hello" as it is in e.g., egrep. + + One question that comes up from time to time is why doesn't S-Lang + simply employ some posix-compatible regular expression library. The + simple answer is that, at the time of this writing, none exists that + is available across all the platforms that the S-Lang library supports + (Unix, VMS, OS/2, win32, win16, BEOS, MSDOS, and QNX) and can be + distributed under both the GNU and Artistic licenses. It is + particularly important that the library and the interpreter support a + common set of regular expressions in a platform independent manner. + + + + 19. Future Directions + + + + Several new features or enhancements to the S-Lang language are + planned for the next major release. In no particular order, these + include: + + o An interactive debugging facility. + + o Function qualifiers. These entities should already be familiar to + VMS users or to those who are familiar with the IDL language. + Basically, a qualifier is an optional argument that is passed to a + function, e.g., plot(X,Y,/logx). Here /logx is a qualifier that + specifies that the plot function should use a log scale for x. + + o File local variables and functions. A file local variable or + function is an object that is global to the file that defines it. + + o Multi-threading. Currently the language does not support multiple + threads. + + + + A. Copyright + + The S-Lang library is distributed under two copyrights: the GNU Genral + Public License, and the Artistic License. Any program that uses the + interpreter must adhere to rules of one of these licenses. + + + A.1. The GNU Public License + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + + + The licenses for most software are designed to take away your freedom + to share and change it. By contrast, the GNU General Public License + is intended to guarantee your freedom to share and change free soft- + ware--to make sure the software is free for all its users. This Gen- + eral Public License applies to most of the Free Software Foundation's + software and to any other program whose authors commit to using it. + (Some other Free Software Foundation software is covered by the GNU + Library General Public License instead.) You can apply it to your + programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + this service if you wish), that you receive source code or can get it + if you want it, that you can change the software or use pieces of it + in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid + anyone to deny you these rights or to ask you to surrender the rights. + These restrictions translate to certain responsibilities for you if + you distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must give the recipients all the rights that + you have. You must make sure that they, too, receive or can get the + source code. And you must show them these terms so they know their + rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, + we want its recipients to know that what they have is not the + original, so that any problems introduced by others will not reflect + on the original authors' reputations. + + Finally, any free program is threatened constantly by software + patents. We wish to avoid the danger that redistributors of a free + program will individually obtain patent licenses, in effect making the + program proprietary. To prevent this, we have made it clear that any + patent must be licensed for everyone's free use or not licensed at + all. + + The precise terms and conditions for copying, distribution and + modification follow. + + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + + + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed + under the terms of this General Public License. The "Program", below, + refers to any such program or work, and a "work based on the Program" + means either the Program or any derivative work under copyright law: + that is to say, a work containing the Program or a portion of it, + either verbatim or with modifications and/or translated into another + language. (Hereinafter, translation is included without limitation in + the term "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of + running the Program is not restricted, and the output from the Program + is covered only if its contents constitute a work based on the Program + (independent of having been made by running the Program). Whether + that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the + Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a + fee. + + 2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, and copy and distribute + such modifications or work under the terms of Section 1 above, + provided that you also meet all of these conditions: + + + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + + + + These requirements apply to the modified work as a whole. If identi- + fiable sections of that work are not derived from the Program, and can + be reasonably considered independent and separate works in themselves, + then this License, and its terms, do not apply to those sections when + you distribute them as separate works. But when you distribute the + same sections as part of a whole which is a work based on the Program, + the distribution of the whole must be on the terms of this License, + whose permissions for other licensees extend to the entire whole, and + thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of + a storage or distribution medium does not bring the other work under + the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + + + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source + code means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to con- + trol compilation and installation of the executable. However, as a + special exception, the source code distributed need not include any- + thing that is normally distributed (in either source or binary form) + with the major components (compiler, kernel, and so on) of the operat- + ing system on which the executable runs, unless that component itself + accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is + void, and will automatically terminate your rights under this License. + However, parties who have received copies, or rights, from you under + this License will not have their licenses terminated so long as such + parties remain in full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and + all its terms and conditions for copying, distributing or modifying + the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted herein. + You are not responsible for enforcing compliance by third parties to + this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot + distribute so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you + may not distribute the Program at all. For example, if a patent + license would not permit royalty-free redistribution of the Program by + all those who receive copies directly or indirectly through you, then + the only way you could satisfy both it and this License would be to + refrain entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is willing + to distribute software through any other system and a licensee cannot + impose that choice. + + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License + may add an explicit geographical distribution limitation excluding + those countries, so that distribution is permitted only in or among + countries not thus excluded. In such case, this License incorporates + the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and + "any later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a + version number of this License, you may choose any version ever + published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by + the Free Software Foundation, write to the Free Software Foundation; + we sometimes make exceptions for this. Our decision will be guided by + the two goals of preserving the free status of all derivatives of our + free software and of promoting the sharing and reuse of software + generally. + + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS + TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, + REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED + TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY + YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these + terms. + + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + convey the exclusion of warranty; and each file should have at least + the "copyright" line and a pointer to where the full notice is found. + + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + + + Also add information on how to contact you by electronic and paper + mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + + + The hypothetical commands `show w' and `show c' should show the appro- + priate parts of the General Public License. Of course, the commands + you use may be called something other than `show w' and `show c'; they + could even be mouse-clicks or menu items--whatever suits your program. + + You should also get your employer (if you work as a programmer) or + your school, if any, to sign a "copyright disclaimer" for the program, + if necessary. Here is a sample; alter the names: + + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + + + + This General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, + you may consider it more useful to permit linking proprietary applica- + tions with the library. If this is what you want to do, use the GNU + Library General Public License instead of this License. + + + A.2. The Artistic License + + + + The "Artistic License" + + Preamble + + + + The intent of this document is to state the conditions under which a + Package may be copied, such that the Copyright Holder maintains some + semblance of artistic control over the development of the package, + while giving the users of the package the right to use and distribute + the Package in a more-or-less customary fashion, plus the right to + make reasonable modifications. + + Definitions: + + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the + basis of media cost, duplication charges, time of people involved, + and so on. (You will not be required to justify it to the + Copyright Holder, but only to the computing community at large + as a market that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it + under the same conditions they received it. + + + + 1. You may make and give away verbatim copies of the source form of + the Standard Version of this Package without restriction, provided + that you duplicate all of the original copyright notices and associ- + ated disclaimers. + + 2. You may apply bug fixes, portability fixes and other modifications + derived from the Public Domain or from the Copyright Holder. A + Package modified in such a way shall still be considered the Standard + Version. + + 3. You may otherwise modify your copy of this Package in any way, + provided that you insert a prominent notice in each changed file + stating how and when you changed that file, and provided that you do + at least ONE of the following: + + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site such as uunet.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + + + + 4. You may distribute the programs of this Package in object code or + executable form, provided that you do at least ONE of the following: + + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + + + + 5. You may charge a reasonable copying fee for any distribution of + this Package. You may charge any fee you choose for support of this + Package. You may not charge a fee for this Package itself. However, + you may distribute this Package in aggregate with other (possibly com- + mercial) programs as part of a larger (possibly commercial) software + distribution provided that you do not advertise this Package as a + product of your own. You may embed this Package's interpreter within + an executable of yours (by linking); this shall be construed as a mere + form of aggregation, provided that the complete Standard Version of + the interpreter is so embedded. + + 6. The scripts and library files supplied as input to or produced as + output from the programs of this Package do not automatically fall + under the copyright of this Package, but belong to whomever generated + them, and may be sold commercially, and may be aggregated with this + Package. If such scripts or library files are aggregated with this + Package via the so-called "undump" or "unexec" methods of producing a + binary executable image, then distribution of such an image shall + neither be construed as a distribution of this Package nor shall it + fall under the restrictions of Paragraphs 3 and 4, provided that you + do not represent such an executable image as a Standard Version of + this Package. + 7. C subroutines (or comparably compiled subroutines in other + languages) supplied by you and linked into this Package in order to + emulate subroutines and variables of the language defined by this + Package shall not be considered part of this Package, but are the + equivalent of input as in Paragraph 6, provided these subroutines do + not change the language in any way that would cause it to fail the + regression tests for the language. + + 8. Aggregation of this Package with a commercial distribution is + always permitted provided that the use of this Package is embedded; + that is, when no overt attempt is made to make this Package's + interfaces visible to the end user of the commercial distribution. + Such use shall not be construed as a distribution of this Package. + + 9. The name of the Copyright Holder may not be used to endorse or + promote products derived from this software without specific prior + written permission. + + 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + + + Table of Contents + + + 1. Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 1.1. A Brief History of S-Lang . . . . . . . . . . . . . . . . . . 4 + 1.2. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 4 + 2. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 2.1. Language Features . . . . . . . . . . . . . . . . . . . . . . 6 + 2.2. Data Types and Operators . . . . . . . . . . . . . . . . . . 6 + 2.3. Statements and Functions . . . . . . . . . . . . . . . . . . 6 + 2.4. Error Handling . . . . . . . . . . . . . . . . . . . . . . . 7 + 2.5. Run-Time Library . . . . . . . . . . . . . . . . . . . . . . 7 + 2.6. Input/Output . . . . . . . . . . . . . . . . . . . . . . . . 7 + 2.7. Obtaining S-Lang . . . . . . . . . . . . . . . . . . . . . . 8 + 3. Overview of the Language . . . . . . . . . . . . . . . . . . . 9 + 3.1. Variables and Functions . . . . . . . . . . . . . . . . . . . 9 + 3.2. Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 + 3.3. Referencing and Dereferencing . . . . . . . . . . . . . . . . 11 + 3.4. Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 + 3.5. Structures and User-Defined Types . . . . . . . . . . . . . . 15 + 3.6. Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . 16 + 4. Data Types and Literal Constants . . . . . . . . . . . . . . . 18 + 4.1. Predefined Data Types . . . . . . . . . . . . . . . . . . . . 18 + 4.1.1. Integers . . . . . . . . . . . . . . . . . . . . . . . . . 18 + 4.1.2. Floating Point Numbers . . . . . . . . . . . . . . . . . . 19 + 4.1.3. Complex Numbers . . . . . . . . . . . . . . . . . . . . . . 19 + 4.1.4. Strings . . . . . . . . . . . . . . . . . . . . . . . . . . 20 + 4.1.5. Null_Type . . . . . . . . . . . . . . . . . . . . . . . . . 21 + 4.1.6. Ref_Type . . . . . . . . . . . . . . . . . . . . . . . . . 21 + 4.1.7. Array_Type and Struct_Type . . . . . . . . . . . . . . . . 22 + 4.1.8. DataType_Type Type . . . . . . . . . . . . . . . . . . . . 22 + 4.2. Typecasting: Converting from one Type to Another . . . . . . 23 + 5. Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . 25 + 6. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 + 7. Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 + 7.1. Unary Operators . . . . . . . . . . . . . . . . . . . . . . . 28 + 7.2. Binary Operators . . . . . . . . . . . . . . . . . . . . . . 28 + 7.2.1. Arithmetic Operators . . . . . . . . . . . . . . . . . . . 29 + 7.2.2. Relational Operators . . . . . . . . . . . . . . . . . . . 29 + 7.2.3. Boolean Operators . . . . . . . . . . . . . . . . . . . . . 29 + 7.2.4. Bitwise Operators . . . . . . . . . . . . . . . . . . . . . 30 + 7.2.5. Namespace operator . . . . . . . . . . . . . . . . . . . . 31 + 7.2.6. Operator Precedence . . . . . . . . . . . . . . . . . . . . 31 + 7.2.7. Binary Operators and Functions Returning Multiple Val- + ues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 + 7.3. Mixing Integer and Floating Point Arithmetic . . . . . . . . 32 + 7.4. Short Circuit Boolean Evaluation . . . . . . . . . . . . . . 33 + 8. Statements . . . . . . . . . . . . . . . . . . . . . . . . . . 34 + 8.1. Variable Declaration Statements . . . . . . . . . . . . . . . 34 + 8.2. Assignment Statements . . . . . . . . . . . . . . . . . . . . 34 + 8.3. Conditional and Looping Statements . . . . . . . . . . . . . 36 + 8.3.1. Conditional Forms . . . . . . . . . . . . . . . . . . . . . 36 + 8.3.1.1. if . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 + 8.3.1.2. if-else . . . . . . . . . . . . . . . . . . . . . . . . . 36 + 8.3.1.3. !if . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 + 8.3.1.4. orelse, andelse . . . . . . . . . . . . . . . . . . . . . 38 + 8.3.1.5. switch . . . . . . . . . . . . . . . . . . . . . . . . . 38 + 8.3.2. Looping Forms . . . . . . . . . . . . . . . . . . . . . . . 40 + 8.3.2.1. while . . . . . . . . . . . . . . . . . . . . . . . . . . 40 + 8.3.2.2. do...while . . . . . . . . . . . . . . . . . . . . . . . 41 + 8.3.2.3. for . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 + 8.3.2.4. loop . . . . . . . . . . . . . . . . . . . . . . . . . . 42 + 8.3.2.5. loop . . . . . . . . . . . . . . . . . . . . . . . . . . 42 + 8.3.2.6. forever . . . . . . . . . . . . . . . . . . . . . . . . . 42 + 8.3.2.7. foreach . . . . . . . . . . . . . . . . . . . . . . . . . 43 + 8.4. break, return, continue . . . . . . . . . . . . . . . . . . . 45 + 9. Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 + 9.1. Declaring Functions . . . . . . . . . . . . . . . . . . . . . 46 + 9.2. Parameter Passing Mechanism . . . . . . . . . . . . . . . . . 46 + 9.3. Referencing Variables . . . . . . . . . . . . . . . . . . . . 48 + 9.4. Functions with a Variable Number of Arguments . . . . . . . . 49 + 9.5. Returning Values . . . . . . . . . . . . . . . . . . . . . . 51 + 9.6. Multiple Assignment Statement . . . . . . . . . . . . . . . . 52 + 9.7. Exit-Blocks . . . . . . . . . . . . . . . . . . . . . . . . . 54 + 10. Name Spaces . . . . . . . . . . . . . . . . . . . . . . . . . 56 + 11. Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 + 11.1. Creating Arrays . . . . . . . . . . . . . . . . . . . . . . 58 + 11.1.1. Range Arrays . . . . . . . . . . . . . . . . . . . . . . . 58 + 11.1.2. Creating arrays via the dereference operator . . . . . . . 59 + 11.2. Reshaping Arrays . . . . . . . . . . . . . . . . . . . . . . 60 + 11.3. Indexing Arrays . . . . . . . . . . . . . . . . . . . . . . 60 + 11.4. Arrays and Variables . . . . . . . . . . . . . . . . . . . . 64 + 11.5. Using Arrays in Computations . . . . . . . . . . . . . . . . 65 + 12. Associative Arrays . . . . . . . . . . . . . . . . . . . . . . 69 + 13. Structures and User-Defined Types . . . . . . . . . . . . . . 71 + 13.1. Defining a Structure . . . . . . . . . . . . . . . . . . . . 71 + 13.2. Accessing the Fields of a Structure . . . . . . . . . . . . 72 + 13.3. Linked Lists . . . . . . . . . . . . . . . . . . . . . . . . 72 + 13.4. Defining New Types . . . . . . . . . . . . . . . . . . . . . 75 + 14. Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 77 + 14.1. Error-Blocks . . . . . . . . . . . . . . . . . . . . . . . . 77 + 14.2. Clearing Errors . . . . . . . . . . . . . . . . . . . . . . 78 + 15. Loading Files: evalfile and autoload . . . . . . . . . . . . . 80 + 16. File Input/Output . . . . . . . . . . . . . . . . . . . . . . 81 + 16.1. Input/Output via stdio . . . . . . . . . . . . . . . . . . . 81 + 16.1.1. Stdio Overview . . . . . . . . . . . . . . . . . . . . . . 81 + 16.1.2. Stdio Examples . . . . . . . . . . . . . . . . . . . . . . 82 + 16.2. POSIX I/O . . . . . . . . . . . . . . . . . . . . . . . . . 84 + 16.3. Advanced I/O techniques . . . . . . . . . . . . . . . . . . 84 + 16.3.1. Example: Reading /var/log/wtmp . . . . . . . . . . . . . . 86 + 17. Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . 88 + 18. Regular Expressions . . . . . . . . . . . . . . . . . . . . . 89 + 18.1. S-Lang RE Syntax . . . . . . . . . . . . . . . . . . . . . 89 + 18.2. Differences between S-Lang and egrep REs . . . . . . . . . 90 + 19. Future Directions . . . . . . . . . . . . . . . . . . . . . . 91 + A. Copyright . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 + A.1. The GNU Public License . . . . . . . . . . . . . . . . . . . 92 + A.2. The Artistic License . . . . . . . . . . . . . . . . . . . . 98 + + + |