elmer user manual:

USAGE: elmer [OPTIONS] -int <interface file> [<python file>...] -int <interface file> - Specifies the interface file to be read which the generated code is derived from (see section on interface files below). <python files> - optional, but required if using -frozen. Specified last on the command line. Lists the Python files which must be frozen into the application (see section on -frozen below). OPTIONS: -O - run embedded Python with optimization level 1 -OO - run embedded Python with optimization level 2 (same as level 1 but does not insert doc-strings into the byte-compiled Python modules) -debug - print every call at runtime made to the embedded Python interpreter -test - read the interface file and print info on it (for verifying interface file correctness) -o <output dir> - Specifies where all Elmer output (generated code) will go. Defaults to the current working directory. -c - C mode...generate glue code to embed the Python module into a C application. Cannot be combined with any other mode. This is the default output mode. -tcl - Specifies that Elmer is to generate code to embed the module into a Tcl application. Cannot be combined with -c. -obj - (for use with the -tcl option only), specifies that Elmer generate a pseudo-object-oriented Tcl interface, similar to Tk, to a class defined in the interface file. Example: $obj method1 arg1 -proc - (for use with the -tcl option only), specifies that Elmer generate a procedural Tcl interface to a class defined in the interface file. Example: classname_method1 $obj arg1 -warm - specifies that Elmer is to generate code which dynamically loads the Python module (and sub modules) into the application at runtime (see section on -warm below). Cannot be combined with -frozen. This is the default. -frozen - specifies that Elmer is to generate code which uses the freeze module (see section on -frozen below). Cannot be combined with -warm. -sp <interface file search path or dir> - Specifies the search path which Elmer uses for locating interface files specified on the command line as well as files specified with the "load" command in an interface file. The search path can be created using a series of -sp <directory> flags, or as a single -sp <search path> flag provided that the search path is valid (uses the proper path seperators). Example: elmer -sp /home/foo/dir1 -sp /home/foo/dir2 is equivalent to elmer -sp /home/foo/dir1:/home/foo/dir2 If not specified, the search path defaults to the current working directory. -debug - Enables debugging statements to be printed at application runtime. The -warm option: By default, Elmer generates glue code for Python modules in "warm" mode. Unlike "frozen" mode (described below), the application will load the Python file(s) which make up the Python module at application runtime. In order to deliver a Python module to an application written in another language, the Elmer-generated C file (possibly compiled into a library), header file, and all Python files which are required to implement the Python module must be provided (in either .py or .pyc format). If delivering Python files is not an option, use the -frozen flag described below. The -warm option is particularly useful for debugging since edits to Python files do not require re-running Elmer and re-building the application. The -frozen option: The freeze module byte-compiles the Python module (and all sub modules) at Elmer runtime (not application runtime) for compilation into the application. This option is useful for delivering modules to be used in other applications by simply providing a single library or C file and a header file rather than the library or C file, header file, and a series of Python files. All Python modules that were frozen are loaded from the application binary and are not dynamically loaded at application runtime. The freeze module follows the Python module loading conventions to find modules and sub modules, in particular, the PYTHONPATH environment variable must be set properly in order for the freeze module to find the necessary Python modules at Elmer runtime (PYTHONPATH is set as a search path). The Python files given as the last arguments on the command line are read by the freeze module. A single Python file is usually specified which typically imports other Python modules. Freeze will find all sub modules from the initial Python file automatically and include them in the application. If additional Python files need to be frozen in which are not explicitly imported anywhere in the Python module, they can be specified on the command line...this is common if the site.py file included in the Python distribution is to be frozen in. If the freeze module cannot locate a Python file that is to be imported into the module (most likely because the file could not be found in PYTHONPATH), it does not get frozen in and Elmer does not produce an error. Instead, the application uses PYTHONPATH to load that particular Python file at application runtime when it is needed, just as it would if elmer was run with the -warm option. WARNING: all changes to Python files which are "frozen in" require re-running elmer with -frozen and rebuilding the application before the changes will be seen in the application if this option is used. The Elmer interface file: Elmer's primary input is an Elmer interface file. The interface file describes the module which Elmer is to generate glue code for and describes the various functions, classes, and data types which the module provides to the application. The following interface file commands are supported: module <module name> The module command defines the name of the module which elmer is interfacing to some other language. A module command must be present in an interface file if it is to be used to interface the module. When interfacing a Python module, the module name must be the same as the Python module file (without the .py extension), exactly like the Python "import" command (the module name is used for the import call to the Python interpreter made by elmer). Only one module command is permitted per file, and only one module name is permitted per module command. If multiple interface files are read (using the "load" command described below), then only the module command in the "top-most" file (the one that instigated the load) is taken...all other module commands in nested interface files are ignored. If elmer is to interface multiple modules into a single interfaced module, a "top-level" Python file which simply imports all of the desired functions, classes, etc. into a single Python module is required. For example, in order to interface everything in module mod1, mod2, and fooClass from mod3, a top-level Python file similar to the one below is required: file: multimod.py ---------------------------------------- from mod1 import * from mod2 import * from mod3 import fooClass ---------------------------------------- ...and the corresponding elmer interface file would reference the interfaced classes and functions through module "multimod": file: multimod.elm ---------------------------------------- module multimod ... ---------------------------------------- load <elmer interface file> The load command is used to bring the interfacing commands from another interface file into the current interface. The argument to the load command can be an absolute or relative path to a file name. The load command uses the interface file search path (defined by the -sp command line option) to locate files. If a file has already been loaded into the elmer session, it will not be loaded again. If a file to be loaded has load commands in it, those files will be loaded as well. Any module commands in loaded files are ignored. If an interface file defines a function, class, or type, those definitions will take precedence over any of the same name in a loaded file, regardless of whether the definition occurred before or after the load command in the file. For example, the interface files below: file: m0.elm file: m1.elm ----------------------- ----------------------- module mymod module myothermod load m1.elm type bar s string getSomeString() type foo [s] load m2.elm load types.elm ----------------------- ----------------------- file: m2.elm file: types.elm --------------------------------- ----------------------- type foo [i] type foo i int getSomeString( string, int ) type bar i --------------------------------- ----------------------- ...result in the following when interface file m0.elm is specified: MODULENAME: mymod TYPES: NAME: foo TYPESTRING: [i] NAME: bar TYPESTRING: s FUNCTIONS: NAME: "getSomeString" ALIAS: "" RETURN TYPE: string ARG TYPES: [] Where getSomeString takes no args and returns a string because the definition in the higer-level file takes precedence, regardless of position. The type foo (the type command is described below) is a list of ints because it has precedence over the definition in the other nested interface file because it was read last. The type bar is a string because it too has precedence over the other definition since it is in a higher-level file. type <type string> The type command allows for the definition of user-defined simple types (classes are defined with the class command described below). The type string is a series of characters, NOT enclosed in single or double quotes, used to define specific types. The following characters are supported: g - guess i - int f - float d - double n - long s - string l - list (elements in list are guesses) m - map (keys and values are guesses) [, ], {, }, and : can be used to define custom maps and lists. The elements inside these characters are "OR'd". Example: [is] - list of ints or strings {g:s} - map with keys which are best guesses, and values which are strings {s:is} - map with keys which are strings and values which are ints or strings Order in the type string defines precedence, for example, type si (ess-eye) will always result in strings, since a number can be in a string, but type is (eye-ess) will result in ints if possible, then a string. The type string cannot be or contain a reference to another type: # # valid - type number is an int OR float OR double OR long # type number ifdn # # invalid - characters n u m b e r will not refer to type number # type numberlist [number] <return type> <function name> ( <arg1 type>, <arg2 type>, ...<argn type> ) [-> <alias name>] Functions are defined by listing the return type, the function name, and the argument types in parenthesis, seperated with commas. An optional alias name can be specified after the -> flag. The alias name can be used to rename a function in the target language. For example, the following interface file definition: float weirdPrefix_computePie() -> computePie ...will map the function weirdPrefix_computePie to the function computePie in the target language, taking no arguments and returning a float. The current version of elmer does not allow the use of line continuation characters when specifying functions. Return types and argument types can be either one of the predefined types, or a user-defined type as specified by the type command (described above). The list of predefined types follows: guess - let the elmer application runtime functions figure it out void - void (empty string in Tcl) int - integer float - floating point double - double precision long - long integer string - character string list - list (if target language supports it) of guessed items map - map (if target language supports it) of guessed keys to guessed items If the target language does not support the type specified, or elmer does not recogonize the type as something defined or predefined, the type is treated as a complex object. Elmer does not consider a mistyped or unrecogonized return/argument type as an error. Instead, the type will be considered a complex object. If the target language is C, all user defined types, guess, list, and map types are treated as complex objects. Complex objects are referenced by handles in the target language. A handle is an int in C and a string in Tcl. The complex object the handle is referring to is maintained in the interpreter which is executing the mapped function calls. For example, if a module is written in Python and Elmer generates the Tcl interface for it, the Python interpreter maintains (updates, garbage collects, etc.) the object which the Tcl interpreter or C program has a handle to. Data type conversions, including the "guess" type are described more below in the "type conversions" section. class <class name> { <function definition 1> <function definition 2> ... <function definition n> } Classes are defined with the class command. The class command takes a class name as an argument, followed by an open brace and one or more function (method) definitions. The close brace signifies the end of the class definition. The current version of elmer requires at least one function definition. The function definitions are identical to the (free) function definition described above. Elmer does not allow class definitions to inherit from others. In the current version of elmer, the module which is being interfaced will have to have every subclass describe each method it can call rather than just those which are unique or overridden. If the target language is Tcl, elmer generates code which implements class objects using an object-oriented style, similar to Tk's implementation of widgets. Example: file: reader.elm ---------------------------------------- module reader type stringlist [s] class reader { reader __init__() -> create void openFile( string ) stringlist getLines() } string getNthLine( stringlist, int ) ---------------------------------------- file: reader.py ---------------------------------------- class reader : def __init__( self ) : self.d_lines = [] def openFile( self, fileName ) : fh = open( fileName ) self.d_lines = fh.readlines() fh.close() def getLines( self ) : return self.d_lines def getNthLine( lines, nth ) : return lines[nth] ---------------------------------------- ...produces "object" commands which can be used in Tcl as follows: % set r1 [reader] __readerObj0 % set r2 [reader] __readerObj1 % $r1 openFile /etc/issue % $r2 openFile /etc/passwd % lindex [$r1 getLines] 1 Red Hat Linux release 7.1sbe (Seawolf) % lindex [$r2 getLines] 14 ftp:x:14:50:FTP User:/home/ftp: % getNthLine [$r2 getLines] 14 ftp:x:14:50:FTP User:/home/ftp: The interpreter which is executing the mapped classes maintains the actual class object (updates, garbage collects, etc.). In this case, Tcl has a command registered under the name of the string handle which refers to the actual object. The registered command accepts initial arguments which must match one of the supported method calls for that object. If the target language is C, elmer does not generate the same object-oriented style code. Instead, the C programmer must use object ids to refer to complex objects (including lists, maps (arrays), and custom types (which appear as native types to Tcl): file: readerMain.c ---------------------------------------- #include<reader.h> /* * main program for testing reader class elmer interfacing */ int main( int argc, char** argv ) { /* * define unique object ids */ int reader1; int reader2; int lines1; int lines2; /* * create the reader instances using the object ids * ...if elNEWID is passed instead of a non-negative ID, * elmer creates a unique id and returns it for future reference */ reader1 = reader_create( elNEWID ); reader2 = reader_create( elNEWID ); /* * open different files for each reader */ reader_openFile( reader1, "/etc/issue" ); reader_openFile( reader2, "/etc/passwd" ); /* * get the lines in each file, reference each with different object ids */ lines1 = reader_getLines( reader1, elNEWID ); lines2 = reader_getLines( reader2, elNEWID ); /* * print out some arbitrary line numbers */ printf( "READER1 LINE: %s\n", getNthLine( lines1, 1 ) ); printf( "READER2 LINE: %s\n", getNthLine( lines2, 14 ) ); return 0; } ---------------------------------------- UNIX> a.out READER1 LINE: Red Hat Linux release 7.1sbe (Seawolf) READER2 LINE: ftp:x:14:50:FTP User:/home/ftp: UNIX> ...notice the use of getNthLine() in the C program which was not necessary in Tcl (but still supported). Type Conversions: The libelmer library is linked into the final application and contains routines which convert data types from one language to another, among other things. When the target language is Tcl, types such as lists, maps, and user defined types are converted using the routines in libelmer. The "guess" type is particularly useful when the source language is something like Python, where the same function can return several different data types. The algorithm for the guess type is briefly described as follows: - try to convert object to an int - if int fails, try to convert object to a double - if double fails, try to convert object to a long - if long fails, try to convert object to a list - if list fails, try to convert object to a string - if string fails, raise exception/return error in target language