parseError 60 evalError 60 0.0 INTRO IC is an interpreted C-style language designed to make it fairly easy to manipulate java objects in a scripted environment. It grew out of a project to write a CD organizer - doing the organizer was easy, but designing what was needed to allow maximum flexibility and generality for the output was not. The result ended up a larger project than the organizer was in the first place. IC is designed to be a C-like language that contains classes that are wrappers for java Objects. Methods can be defined to access and manipulate these Objects, and process the results. The C-like syntax means manipulating the data is easy, and the ability to add new functions/methods without touching the java code means that if any needed function cannot be done with the existing libraries new functions can be defined. IC stands for several different things: it could be "Interactive C", it could be for "Insulating Cup" (since it sits between you and the java) or "Instant Coffee" (though a quick search shows that one is already taken.) If the "I" is read as an "l" it could stand for "leisure Consumer", which it has been for a while. 1.0 LANGUAGE BASICS The language is very similar to C, with several relatively small differences and additions. First, the similarities: IC has all the standard C operations, including arithmatic, logical, and binary operations, pre- and post-increment and decrement, and assign ops. It has the same control structure - for, while, and do loops, switch, if, return, continue, and break are all supported. Functions can be defined and called. While there is not a preprocessor as such, several of the preprocessor command are supported. In particular, #if, #elseif, #else, and #endif are supported. There is no #defines (that would have caused problems with finding the error lines) so there is no #ifdef, but a macro called "defined" is automatically defined, which will tell if a given name is a class, instance variable, method, global variable, local variable, function or macro, or undefined. This can be used with the #if directive to approximate the #ifdef directive. Files can be included as with the include directive. (there is no # in front of the "include".) In addition, there is a directive "#eval", which causes the expression following it to be evaluated immediately during the initial parse phase, rather than during the second evaluation phase. This is useful for setting some global variables, debugging statements, and so one. There is also a "#debug" command that causes the integrated debugger to be entered when the following statement is executed. As for differences, one is that typed variables are not required. Variables must be declared, but type information is optional. If it is given then assignments must match, but if not given any value will be accepted. When using IC as a scripting language it can be convenient to leave variables untyped, while when writing a larger program typing is useful. By setting the global variable _STRICT you can require that all variables must have type information. Declarations must begin with one of the keywords "global", "local", or "var", depending on whether they are global variables, local variables, or instance variables. This keyword is followed by an optional type, which can be one of the predefined types "int", "array", or "string", or a name of a defined class. There are no C-style pointers. One major feature that has been added is object support. Objects can be defined to have methods, and both static and instance variables. The main purpose of objects is to act as wrappers for Java objects, allowing them to be accessed and manipulated from within the interactive scripting environment. Function calls are more flexible than in C. Functions (methods) can be invoked in their own frame, as is usually done, or in the frame of the calling context, either as functions or macros. Arguments can be passed by value or by reference, and default values for missing arguments can be set. The SWITCH statement has been enhanced. For one, a case ending with "::" rather than ":" will have an automatic BREAK added at the end. The cases are not restricted to integers, but can be any values. Even more, they can be statements implicitly containing the case - for instance, if the case statement is case >5: then the selection will hold for all values > 5. For example: switch (i) { case 5:: // does just this if i == 5 (autobreak) case >5: // does this if i > 5, and falls through case <= 0::// does this if i <= 0 (or == 5) and breaks case & 2:: // does this if bit 1 is set (in this case // just for 2 and 3) and breaks default: // remaining cases: 1, 4 } Because the language is interpreted, it is possible (and useful) to generate code that is then executed. This is one of the strengths. 1.1 DATA TYPES Variables may be typed, but are not required to be so. If a declaration is typed then all assignments to it must be of the declared type. If it is not typed, any of the available data types can be assigned (but see the section on "_STRICT"). The data types supported are numbers (integers), strings, arrays, and instances. Conversion is performed as determined by the operators: for instance, addition on numbers is regular numeric addition, but a string added to an integer (in either order) converts the integer to string and appends the two strings. New complex data types can be added by defining classes. It would not be much work to add more simple data types as well (float, for instance). I may do this in the future. 1.1.1 NUMBERS Numbers (integers only) are constants entered like in C: the regular decimal number can be entered, or an octal number preceeded by a "0", or a hex number preceeded by "0x" or "0X". Decimal points and scientific notation are not allowed. 1.1.2 STRINGS Strings are entered using quotation marks. Either single or double quotes are accepted - this is convenient for a scripting language to allow embedded quotes. Recognized escapes include both quotes, "\n" and "\t", and "\\". /** temporarily(?) removed Also, strings can be accessed with use the same square-bracket notation used for arrays. The Nth character of a string, 0-based, can be obtained by string[n], and the substring from n to m can be gotten with string[n, m]. HOWEVER, currently they cannot be manipulated (assigned to) the same way. I am trying to decide whether to live with this inconsistency, disable the assignment method for arrays as well, or implement it for strings. In the meantime use library functions to manipulate strings. */ 1.1.3 ARRAYS Arrays are 0-based arrays of other data items. These other items themselves can be arrays, so nested arrays are supported. Arrays are entered using square brackets, so to initialize a nested array you could, for example, type [1, 2, [3, 4, 5], 6] They are also accessed using square bracket notation, so the first element of array ARRAY would be ARRAY[0]. If ARRAY were initialized to the preceeding value, the value of ARRAY[2][1] would be 4. In addition, subarrays can be accessed by passing a range of arguments, ARRAY[start, end], which will return the array between [start, end). Arrays can be initialized in 2 different ways: either by literal assignment (that is, array = [1, 2, 3]) or by using the library ARRAY function to initialize its size (array = array(3);). They must be allocated before use. 1.1.4 INSTANCE Instances can be stand-alone, but more likely will be associated with a Java class. In this way instances of the Java class can be accessed from within the scripting language. It also allows them to be used as arguments or return types to method calls, since if a wrapper class is defined for a Java object it can be passed back and forth between the Java application and the IC script. An Instance has a Class, and the class consists of 3 main parts: its instance variables, its method definitions, and an associated Java class type. Any (or all:) of these may be missing. There are 2 ways to create Instances. One is by use of a constructor function, signalled by use of the keyword "new", as in Java. More commonly (probably) would be the use of an external or builtin function, which could then fetch or create the Java object for the instance to wrap. 1.2 FUNCTIONS Like in C or Java, functions can be defined and called in IC. There are several changes that allow things that are not possible (or relevent) in those languages. Arguments can be passed by value or by reference. By default they are passed by value (which means assignments made in a function will be lost when the function returns). By preceding a variable in the calling statement with a "&" the call is made by reference. This means that if the value is reassigned in the function, the new value will be the value of the symbol in the calling function as well. A function can be defined to have default values for the arguments. Any argument can have a default set in the function definition by putting an "=" after the argument and putting the desired default value there. When a function is called and that argument is omitted (either not in the argument list, or represented by nothing after a comma) the default value is used. There are several different possibilities when defining functions. First, they can be broken down into functions or methods. These each can be defined to be functions or macros. And finally, a function/method can be defined, external, or built-in. 1.2.1 METHOD OR FUNCTION A method is a function called with an instance in its calling environment. The instance has its own associated symbol table and a function table associated with its class, which are prepended to those that would be the environment otherwise. This means within a method a function call can refer to a method for the current object or a function. Methods can also be called on an explicit object using dot notation, just like in Java. (currently I have not defined static instance variables and static methods, but may in the future.) 1.2.2 MACRO OR FUNCTIONS In a function call, the arguments in the call are evaluated and values assigned to the associated symbols in the function definition. A new stack frame is initialized with these symbols and any local variables, and this is the execution environment of the function. Macro calls are different in two ways. First, in the symbol assignment the expression giving the value is not evaluated, but assigned directly - in effect, replacing the occurence of the symbol where it appears. Second, no new stack frame is pushed. The argument symbols are temporarily prepended to the existing symbol table, which remains in effect. This means symbols in the calling environment are accessible in the macro even if they were not passed as arguments. 1.2.3 BUILT-IN, EXTERNAL, OR DEFINED FUNCTIONS These describe the way the function is defined. The simplest is the defined function. A defined function is made up of IC statements inline, and looks pretty much like a C function definition. When it is called the IC statements are executed in order, using the current symbol table. External functions are stubs that call specially written java objects that know about IC and possibly some external objects. These functions are passed a pointer to the current symbol table through which they can access the current values of the variables. They can do whatever processing they need to in Java, and then return the result as an IC object to the program. Besides needing to be declared public, there must be a public constructor with no arguments so a blank instance can be created. The released program includes a library of external functions to do common operations like string manipulation, reading and writing files, and so on. Finally, built-in methods allow you to call a Java method directly from within an IC program, with certain limitations. Basically these limitations are that the method and object must be declared public, and the arguments and return types must all be of the supported types. Currently, in the initial version, supported types include int, String, any class that has an IC wrapper class designed for it, and any Collection of supported classes. In addition, builtin functions cannot be macros (since the Java program knows nothing about the IC symbol table) 1.3 STATEMENTS An IC program is made up of one or more statements. These statements are parsed, put into expression trees, and executed. Top level statements can be of several different types: INCLUDE statements, GLOBAL statements, CLASS statements, FUNCTION or MACRO statements, or C (style) statements. 1.3.1 INCLUDE STATEMENT The INCLUDE statement names a file to include as if its contents were inserted directly in the source file. Its structure is include FILENAME; The given file is read and parsed in place, and its contents will affect the program execution environment. 1.3.2 GLOBAL STATEMENT A program may have 0 or more global statements. A global statement declares and optionally initializes one or more variables. Since IC is untyped there are no type declarations to add, just the variable name. Multiple variables may be entered on one line separated by a ",", and values may be assigned by adding an optional "=VALUE" after each variable declaration. global var[=xxx] [, var1 [=xxx1]]*; Initial values are evaluated at the time the variable is declared. 1.3.3 CLASS STATEMENT 1.4 OTHER STUFF There are several features that are intended to make the IC environment more usable. 1.4.1 STRICT TYPING Normally, variables may be given an optional type when they are declared (in the GLOBAL, LOCAL, and VAR statements, and in function/macro definitions), and any assignment that does not meet this type throws an error. If strict typing is turned on by setting the predefined global variable _STRICT to non-zero then all variable declarations must be typed. Any declaration of a variable or assignment to a preexisting variable that does not meet this criterion will cause an error. This can be preceeded with the "#eval" (immediate) operator to make sure it is the first thing executed in the file. There is no type casting, but conversion functions for the primitive types are defined in the standard library, and any more needed can easily be written. 1.4.2 SPECIAL VARIABLES AND FUNCTIONS IC has the concept of "special" variables and functions, similar to Lisp advised functions. A special variable is one that calls a handler function before the variable is assigned a new value; a special function is one that calls a handler function either just before being invoked, or just after being invoked. 1.4.2.1 SPECIAL VARIABLES Special variables are set with the SPECIAL library function (see below). This associates a handler function that is called before each assignment is done to the variable.The handler function is called with 2 arguments: the first is a string giving the name of the variable, and the second is the value the variable will be assigned. The actual value assigned will be the value returned by the handler function, so this allows the value to be changed before it is set. This works (should work) for all assignments, including autoincrement and decrement and op= assignments; 1.4.2.2 SPECIAL FUNCTIONS Special functions are similar to Lisp advised functions. They are routines that run before or after another function is called. For syntax see the SPECIAL command in the LIBRARY section. A BEFORE special function is called with the same calling environment as the function - essentially, a macro. It takes no arguments, but can access the arguments passed to the main function as if they were local variables (though they are not declared). They operate in the same environment as the function, which means the arguments can be changed in the BEFORE function and the adjusted values will be passed to the function. AFTER special functions are called when the main function has completed processing. These functions take a single argument, which is the return value of the original function. This value may be examined and processed. The return value of the AFTER function replaces the return value of the original function. Functions may have both BEFORE and AFTER special functions. 2.0 LIBRARY Besides the language, IC includes a library of predefined useful functions for doing standard tasks. While it is easy to write your own functions, some tasks are so common it is best to provide them in a system library. The functions documented here may be added to in the future as I determine more functions are useful. 2.1 STRING FUNCTIONS 2.1.1 substring(STRING START, END, FROMEND, RELATIVE): Finds the requested substring of the given string - STRING: String to take a piece of - START: Start of substring (but see FROMEND) - END (optional): End of substring. If omitted defaults to end of string - FROMEND (optional): If set then the START pointer is used to measure from the end of the string rather than the beginning. The default is from the beginning. - RELATIVE (optional): If set then the END parameter is taken to mean the length of the desired string rather than the end pointer. The default is end pointer. 2.1.2 index(STRING, TARGET, START): Finds a given string within another - STRING: the string to search in - TARGET: The string to search for - START (optional): The location to start, defaults to 0 2.1.3 split(STRING, SEP): Splits a string using a given regular expression, just like the java String.split() method. - STRING: the string to split - SEP: the seperator RE 2.1.4 trim(STRING, CHARS, FRONT, BACK): Strips unwanted characters from the front and back of a string. - STRING: The string to strip - CHARS (optional): Characters to strip. If left default it does the Java behavior, strips off all control chars (value <= ' ') - FRONT (optional): If TRUE strips from the front, if FALSE does not do the front. Default is TRUE. - BACK (optional): If TRUE strips from the back, if FALSE does not do the back. Default is TRUE. 2.2 IO FUNCTIONS 2.2.1 print(ARG): Prints the given argument - ARG: argument to print out 2.2.2 println(ARG): Prints the given argument with a CRLF after it. - ARG: argument to print out 2.2.3 read(FILE, SIZE): Reads a file and returns the contents as a string. - FILE: The file to attempt to read - SIZE (optional): The maximum size to read, defaults to -1 (unlimited). 2.2.4 write(FILE, CONTENTS, APPEND): Writes the given string into the file. - FILE: File name to write to - CONTENTS: The string to write - APPEND (optional): If true, append the string to the file. Default is false (overwrite). 2.3 LANGUAGE FUNCTIONS: 2.3.1 size(VAR): Gets the size of an array or string. - VAR: A variable to measure. If an array it gives the number of elements; if a string the string length. Anything else returns -1. 2.3.2 type(VAR): Gets a string describing the type of a variable. Values are "number", "string", "array", or "instance of XXX", where XXX is a defined class. - VAR: Variable to get the information for 2.3.3 defined(VARNAME): Returns the defined type of the given variable, or an empty string if undefined. The return value is one of "class", "instance variable", "method", "global variable", "variable", "macro", or "function". This can be used with the #if directive to control conditinoal interpretation. It is defined as a macro so that it can recognize local variables in the calling frame. - VARNAME: A name to check the status of. It can be 2.3.4 array(SIZE, DEFAULT): Creates an array. - SIZE: Gives the dimension of the array. If this is an INT it gives the length of an array; otherwise, it should be an array itself giving the length of each of the dimensions. This can be done inline - for instance, to get a 3-dimensional array with dimension length A, B, and C, you could call array([A, B, C], default) - DEFAULT (optional): Default value to use. If not set uses 0. 2.3.5 eval(STRING, CURRENT): Evaluates the string in the interpreter - STRING: The string to evaluate - CURRENT (optional): If nonnull, evaluates in the current environment (using the current symbol tables and function tables). Functions defined will remain in the environment. If FALSE the expression will be defined in a new, blank environment. The default is FALSE. 2.3.6 meval(STRING): Evaluates the string in the interpreter. This is a macro rather than a function, and so is evaluated in the context of the calling function. That means the existing local symbol table remains available. Variables not passed to this macro are available inside it, and assignments made will not be lost when the statement completes. This is different from EVAL above in that the variables are not assigned until evaluation time. - STRING: The string to evaluate 2.3.7 apply(STRING, ARGS, INSTANCE): Finds the function/method STRING and applies it to the arguments given in the array ARGS. - STRING: The name of the function/method to call - ARGS: an array giving the arguments to pass to the function. This must be an array. - INSTANCE (optional): an object to pass the STRING message to. If this is not an object (the default) STRING must name a function. If it is an object that object is checked for a method STRING, and that is called. 2.3.8 toStr(LEAF): Converts a type into string, like casting in Java. Will throw an error if no conversion is available. - LEAF: the value to convert 2.3.9 toInt(LEAF): Converts a type into number, like casting in Java. Will throw an error if no conversion is available. - LEAF: the value to convert 2.4 OTHER FUNCTIONS 2.4.1 error(REASON): Throws a user error. - REASON (optional): Any information you care to include in the exception. The default is "Error". 2.4.2 catch(EXPR): Evaluates the passed expression and catches any error that is generates. This is a macro, which means EXPR is not evaluated when it is passed, but instead when it is invoked. The return value of CATCH is "" if there is no error, or the text description of the error if one occurred. If you want to keep the result of EXPR you can make an assignment within it - for instance, if the statement is catch(f1(2)); you can replace it with catch(a = f1(2)); (when a is a declared variable, of course) to get the value. Also, arguments need not be only C expressions. C blocks are allowed too - that is, the following is legal: catch({for (i = 1 to 10) {if (i % 2) a = a + 2;}}) is OK. No new variable declarations can be made, and each line (including the last) must end with a ";". - EXPR: an expression to evaluate 2.4.3 getFile(DUMMY): returns the file and line the given variable is defined on. It is most useful just to stick in any value like 1 or "xxx", which will give the current file and line of the program executing. The return is an array of structures, each structure having values for "file" and "line". The first such structure is the current file. - DUMMY: any value. The actual return gives the location this value was created at. 2.4.4. special(TYPE, NAME, HANDLERNAME): Makes a variable or function special - that is, designates a handler function to be run just before or after the function (variable) is accessed. This function can adjust the arguments and return value of the original function, or change the value assigned to a variable. - TYPE: Tells the type of the special function. This must be on of the three values "VARIABLE", "BEFORE", or "AFTER" (ignoring case) - NAME: the name of the variable or function to make special. This can be any variable or function in current scope. - HANDLERNAME: The name of a function (macro) currently in scope that will become the handler function. For variables, this will be called before the variable is assigned to, and its return value will become the new value of the variable. For functions, this will be called either before or after the function is called. If before, it shares a calling environment with the function, and so can access its arguments. If after, it takes a single argument which is the return value of the funciton, and its return vaue replaces the original return value. 2.4.5 getCallStack(): Useful for debugging, this function returns an array of structures giving the function name and line number for each frame on the call stack. 2.4.6 exit(CODE): Exits the Java virtual machine - CODE: The result code to return to Java 3.0 INVOKING 3.1 RUNNING AS AN APPLICATION To run as an application, the program is called java IC.Program FILE ARG* where FILE is the name of the file containing the commands to execute. The commands can be function or class definitions, source files to include, global definitions, or C expressions. All expressions are collected and put in an unnamed block, which is executed after the entire file is parsed. Optional arguments can follow the file name, and any that do will be put in an array _args[] available as a global variable. There are a few fallbacks if FILE cannot be found. First it looks for the file FILE based in the current directory. If it cannot find that it looks for FILE.ic. If it still cannot find it, it will look at the class path and look in each of them to see if it can find the file. JAR files will be checked as well, and the file extracted and read in if it is there. 3.2 API Once the source is fully documented this will not be needed, but as of now it still is. 3.2.1 PROGRAM OBJECT The first step is to create a program object. This is done very simply with the constructor: Program program = new Program(); 3.2.2 READING A PROGRAM To execute a file it must first be read in. This is done with the READ method. Program read(String filename) throws CompilerError; - FILENAME: The file to read The files are cumulative; that is, once one is read in to a Program object it remains in the environment, and subsequent READs add to it. There is no way to clear a Program, but they are simple to make, so you can just create a new one. 3.2.3 EVALUATION 3.2.3.1 EVALUATING THE ENVIRONMENT Leaf eval() throws CompilerError; When a file is read in with the READ method global, function, and class definitions are read in. Any other lines are considered to be executable, and are gathered into a block. EVAL executes that block. Note that if you run EVAL multiple times the same commands will be executed each time. If a second file is read with the READ command its statements will be appended to the statements from the first file. The statements can be cleared with the CLEAR command; there is no way to delete globals, classes, or functions except for starting over with a new Program. 3.2.3.2.EVALUATING AN EXPRESSION Leaf eval(String expr) throws CompilerError; This parses and evaluates the given string in the current program context. Changes to the environment are permanent - functions can be defined, global variables defined or changed, and so on. 3.2.3.3 CLEARING THE MAIN BLOCK Program clear() This clears the executable statements out of the main program block. It does not clear or reinitialize classes, global variables, or the function table. 4.0 OTHER FEATURES 4.1 DEBUGGER There is a simple debugger included, along with hooks to make it much nicer. To use the debugger include the file debug.ic in your source. This defines several functions and system variables that are used to run the debugger. To set breakpoints use the #debug directive in your source code just before the line you want to start debugging. 4.1.1 SPECIAL VARIABLES There are 2 variables that are used to control the debugger. 4.1.1.1 _DEBUG_LEVEL This controls the behavior of the debugger. It takes numeric values: - 0: Debugging is off - 1: The interpreter will stop at designated breakpoints - 2: The interpreter will single-step, jumping over function calls. (NEXT) - 3: The function will single-step, stepping into functions (STEP). Note that it does not step into builtin or external functions, since they execute in Java. It only steps into defined functions. By default it is set to 1 when the debug.ic file is loaded. Normally you should not have to touch this directly, the standard debug function uses this to control behavior. 4.1.1.2 _DEBUG_FUNCTION This names a function to call when a breakpoint is hit. The default function, _debug, is defined in debug.ic. The function is called with the following signature: DEBUG(string BUFFER, int LINE, int PTR) - BUFFER: string giving the current source code - LINE: line number currently executing - PTR: current character offset in BUFFER It is very easy to change the debugger behavior by replacing this function. 4.1.2 DEBUGGER COMMANDS There are several commands that the debugger handles. More can be added by augmenting or replacing the _DEBUG_FUNCTION function. - print (abbreviated p): evaluates and prints an expression. It can be used to examine and change values within the program. - cont (abbreviated c): Continues to the next breakpoint - next (abbreviated n): Single steps, jumping over functions - step (abbreviated s): Single steps into functions 5.0 TO DO There are a few features that would be nice, but I don't need them now and they are not worth adding unless someone needs them. These include things like static functions, and more data types (maybe a library of Java Object wrappers). It would be nice for error handling to continue through the parse phase, rather than quit at the first error as it currently does. Error messages could be clearer and more standard. Believe it or not, they mean something to me, but I bet most of them don't to you. However, the error line should be useful. A.0 SYNTAX (not finished) *** GENERAL *** EXPR = (C expression;) | {EXPR*} NAME = [_a-zA-Z][_a-zA-Z0-9]* CLASSNAME = NAME(.NAME)* DECL = NAME [= EXPR] DECLS = DECL (, DECL)* FILECHARS = (NAME | .) FILE = FILECHARS+(/FILECHARS+)* *** TOP LEVEL *** TOPLEVEL = (GLOBAL | INCLUDE | FUNCTION | EXPR | CLASS)* GLOBAL = global DECL; INCLUDE = include FILE; ARG = (NAME | &NAME | NAME=EXPR) ARGS = [ARG (, ARG1)*] LOCAL = var ARGS; FUNCTION = EXTERNAL | DEFINED DEFINED = (function | macro) NAME(ARGS) {(LOCAL | EXPR)*} EXTERNAL =(function | macro) external NAME(ARGS) CLASSNAME [ENTRY**]; ** if ENTRY is not given then NAME is used as the java method name *** CLASSES *** CLASS = class NAME [wraps CLASSNAME | extends NAME] {(IVAR | METHOD)*} IVAR = instance DECLS; METHOD = (MDEF | MEXT | BUILTIN) MDEF = (method | macro) NAME(ARGS) {(LOCAL | EXPR)*} EXTERNAL =(function | macro) external NAME(ARGS) CLASSNAME [ENTRY**]; TYPE = int | String | CLASSNAME TYPES = [TYPE (, TYPE)*] BUILTIN = method builtin NAME(TYPES) [ENTRY**]; ** if ENTRY is not given then NAME is used as the java method name B.0 COMPARISONS After writing IC, a web search turned up several similar programs. In some ways this made me feel pretty good - the existence of multiple languages similar to IC indicates it does inded fulfill a need. It also indicates I am not going to get rich off the proceeds from this project, and indeed with a choice of similar programs it is not even a given that anyone would choose IC over the other ones. I did find a comparison of the others on Javaworld.com. (http://www.javaworld.com/javaworld/jw-04-2002/jw-0405-scripts_p.html) It compares several alternatives, namely Jacl, Jython, Rhino, and Beanshell. The results are available online, but here is a summary. I hate to say "better" or "worse". IC has some strengths, some "apples and oranges", and some weaknesses. The author compares the packages on 5 issues. I cannot be as objective as he was, but will intrude my thoughts on IC into his conclusions. B.1 INTEGRATION All the interpreters he found easy to integrate, and would not find IC otherwise. Jython and Jacl require more knowledge of non-java languages, which some might consider good and others bad. IC is pretty basic java and C, so it should not have an issue here.Following are the comments on the other languages. B.1.1 JACL If you desire Tk constructs in your scripts to create user interface objects, look at the Swank project for Java classes that wrap Java's Swing widgets into Tk. The distribution does not include a debugger for Jacl scripts. B.1.2 JYTHON Supports scripts written in the Python syntax. Instead of using curly braces or begin-end markers to indicate flow of control, as many languages do, Python uses indentation levels to show which blocks of code belong together. Is that a problem? It depends on you and your customers and whether you mind. The distribution does not include a debugger for Jython scripts. B.1.3 RHINO Many programmers associate JavaScript with Webpage programming, but this JavaScript version doesn't need to run inside a Web browser. I found no problems while working with it. The distribution comes with a simple but useful script debugger. B.1.4 BEANSHELL Java programmers will immediately feel at home with this source interpreter's behavior. BeanShell's documentation is nicely done, but don't look for a book on BeanShell programming at your bookstore -- there aren't any. And BeanShell's development team is very small, too. However, that's only a problem if the principals move on to other interests and others don't step in to fill their shoes. The distribution does not include a debugger for BeanShell scripts. B.2 PERFORMANCE Javaworld ran 4 very short benchmark tests on the 4 different languages to compare their speed. They were all run on the same machine, a 700Mhz Pentium III with 256MB. I ran the same tests on my development machine, a 600 Mhz Celeron with 64MB. Needless to say this affects the speed. Following are test descriptions, and the results come afterwards. The author does not state what time measure he used; I used user time for IC, which means real time tends to be a second longer. The test code was very simple for all tests, and varied only in the minor syntactic differences required for the respective languages. The versions given below are the IC versions. B.2.1 LOOP COUNTING FROM 1 TO 1000000 Code: function count (howmany) { local i; for (i=1; i < howmany; i++) { } } global bench = 1000000; println("counting to " + bench + "..."); count(bench); B.2.2 COMPARE 1,000,000 INTEGERS FOR EQUALITY Code: function compare (howmany, value) { local i; for (i = 1; i < howmany; i++) { if (i == value) { println(i); } } } global bench = 1000000; print("comparing an int against " + bench + " others..."); compare(bench, bench - 1); B.2.3 ALLOCATE AND INITIALIZE A 100,000 ELEMENT ARRAY Code: function allocArray (howmany) { local j, arr; arr = array(howmany); for (j=1; j < howmany; j++) { arr[j] = j; } } global bench = 100000; println("allocating and initializing a " + bench + " element array..."); allocArray(bench); B.2.4 ALLOCATE AND INITIALIZE A 500 X 500 ELEMENT ARRAY Code: function allocMatrix (howmany) { local matrix; local i, j; matrix = multiarray(0, howmany, howmany); for (i=1; i < howmany; i++) { for (j=1; j < howmany; j++) { matrix[i][j] = i; } } } global bench = 500; println("allocating and initializing a " + bench + "x" + bench + " element array..."); allocMatrix(bench); B.2.5 PERFORMANCE SUMMARY Here are the performance summaries. IC stacks up quite well - running on a machine with a 15% slower clock speed, 1/4 the memory (really inadequate to run Java), and on a lower processor, it falls consistently behind Jython, around even with Rhino, and well ahead of BeanShell and Jacl. Scripting TEST TEST TEST TEST interpreter 1 2 3 4 Jacl 140 300 25 45 Jython 1.2 4 1 1 Rhino 5 8 1.3 7 BeanShell 80 80 22 18 IC 6.2 9.1 2.3 5.0 B.3 EASE OF INTEGRATION He measures integration in 2 ways, instantiating and using the interpreter, and doing some simple GUI tasks. In the first category all do reasonably well, and IC is as good as any of them. The syntaxt is C, along with a few minor additions, so it is comparable to BeanShell and Rhino. The other 2 are based on other languages. The second depends on the availability of libraries, and here IC lags. I have not written any other libraries than some simple functions I needed for the project I was doing. Furthermore, IC dos not support operator overloading or all the primitive types Java does, so writing your own libraries is not really an option. I see this as limiting the scale of projects using IC. No one will want to write a large GUI in IC without AWT libraries, but for smaller projects it is fine. The library code is simple to write and does not required writing or compiling Java code, so it is certainly feasible. Users looking for complete, preexisting libraries should look elsewhere. B.4 LIBRARY SUPPORT ISSUES Here again using the measurement of library support IC does not fare well. However, he also talks about complexity: It is also worth checking to see how much source code it took to implement the interpreter. It's unrealistic to think that you can understand an interpreter's every code line and extend it or tune it to fit your needs. The interpreters are just too big. Still, it's worth knowing these interpreters' sizes. At some point you may need to modify the interpreter source code or dig through the code to figure out the interpreter's behavior. I include the quote because this is a real strong point for IC. In the current release there are fewer than 4000 lines of code. Most objects are small, and the overall design is very simple to follow. Modifying or (horrors) fixing IC is child's play compared with the other large projects. B.5 LICENSING All are GPL, and so there is no real difference to consider here. B.6 SUMMARY (MINE, NOT HIS) IC is designed to be a simple, basic language (just like C originally was), with various decisions made based on the following criteria: - C standards - Scripting ease - Extensibility and Java class support - Simplicity Features that could be added to the basic language without violating any of these features (in particular number 4) were added. Some of the major features and changes include: - No variable typing - Optional function parameters - Macro capability Results of these decisions include: - exception-driven error handling - no overloaded functions Other strengths of the implementation include interpreter simplicity and extensibility. C.0 OBJECT STRUCTURE C.1 INTERFACES: Assignable: Can be assigned to by the ASSIGN operator Constants: Everything implements Constants Statement: Implements errorInfo and Constants errorInfo: Carries source location information in case of error C.2 CLASSES Following is an object tree for the classes giving the relationships between the objects. The initial letter gives the interfaces, with the following additions: every object implements Constants; Statement implements errorInfo and Constants, so those two are not listed separately. Classes followed with a * are abstract. Program E Input SymbolTable FunctionTable S Node * S Expression S BinaryNode * S AndOr SA Assign S Bitwise S Comma S Compare S Divide S Logic S Minus S OpEquals S Plus SA Reference S Remainder S Times S Leaf * S Array S Block S Instance S This S Int S Null S Str SA VariableRef SA ArrayRef S Call S New S Negative S Not S AutoIncrement S QuestionColon S Declare Symbol Alias CompileError * EvalError ParseError ReturnException UserException LoopException S Function * S FunctionDef S ExternDef S BuiltinDef S Thingy S Primitive S Untyped S Do S For S If S While S LoopControl S Return