The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Inline::SLang::Details - How Inline::SLang works

DESCRIPTION

This document is intended to provide details of how Inline::SLang weaves its magic. It's probably going to be most use in cases when you find apparently bizarre behaviour and wonder if it is a bug or a feature.

The implementation for Inline::SLang was initally based on the code found in Neil Watkiss' Inline::Python and Inline::Ruby modules. However, all bugs are my own creation.

What happens when Perl starts?

The S-Lang interpreter is initialized using the SLang_init_all(), SLang_init_array_extra(), and SLang_init_import() routines. These are used to ensure that all the functions in the S-Lang Run-Time Library (http://www.s-lang.org/doc/html/slangfun.html) are available. An error handler is set up (using the SLang_Error_Hook variable in the C library) which restarts the S-Lang library, clears the S-Lang error variable, and then calls Perl's croak() routine with the S-Lang error message. This allows users to trap S-Lang errors from Perl code using eval(); for instance

  # Evaluate a S-Lang statement which contains an error
  #
  eval { Inline::SLang::sl_eval( "variable = ;" ); };
  print "The S-Lang error was:\n$@\n";

will result in the following message printed to the screen:

  The S-Lang error was:
  S-Lang Error: Syntax Error: Expecting a variable name: found '=', line 1, file: ***string***

The initialization of the S-Lang interpreter is done in the BOOT section of Slang.xs.

Additional initializtion is performed if the SETUP configuration option is set to slsh, which is its default value. This step is performed by Perl just prior to loading the user-supplied S-Lang code (see the functions build() and load() in Slang.pm). If the option is set, additional changes are made so that the environment of the S-Lang interpreter matches that used by the slsh interpreter from the S-Lang distribution. These changes are listed below and are currently "UNIX centric", although they should be made to work on all systems in the long run. Note that the S-Lang functions and variables created by slsh - e.g. atexit(), exit(), and __argv - are not created by this code.

  1. The load path for the S-Lang library is set to the contents of the SLSH_PATH environment variable. The path can be queried and changed using the get_slang_load_path() and set_slang_load_path() functions from the S-Lang Run-Time Library.

  2. The system configuration file (slsh.rc) is evaluated. The file is searched from the colon-separated directly list stored in (only the first value that is defined is used in the search):

    • The environment variable SLSH_CONF_DIR.

    • The environment variable SLSH_LIB_DIR.

    • The set of directories: /usr/local/etc:/usr/local/slsh:/etc:/etc/slsh.

  3. The user configuration file (.slshrc) is evaluated if one exists in the directory given by the environment variable HOME.

If the Perl code has not been evaluated before - or if it has been changed since the previous run - the Inline code kicks in to evaluate the S-Lang code. This involves:

  1. The S-Lang interpreter is queried to find out all functions that are defined in the namespaces listed in the BIND_NS configuration option. The user-supplied S-Lang code is then evaluated and the same set of namespaces are again queried for the names of defined functions. We also pick up any new namespaces that may have been defined if the BIND_NS option is set to All.

    The names of the new functions - i.e. those functions added by the user-supplied S-Lang code which the S-Lang function _apropos() lists when the flag value is set to 3 - are stored for later use.

    Complications:

    • The _inline namespace is ignored since this is used by the module and should be considered off-limits.

    • The list of functions in bind_slfuncs is added to the list of functions to bind. This list can include functions defined as part of the S-Lang Run-Time Library.

    • The fact that the module allows users to change the name that namespaces and (some) functions have when mapped to Perl.

  2. The list of defined S-Lang data types is found. This includes user-defined types added by any imported modules and "named" structures created via a S-Lang typedef statement.

    Utility functions are created in Perl in the Inline::SLang package which are wrappers around calls to the DataType_Type constructor. This allows users to say Inline::SLang::UShort_Type() rather than DataType_Type->new("UShort_Type").

    For those types we do not recognise - essentially all user-defined types - we create objects with names equal to the S-Lang variable name. The objects have very few methods, and are just a way of carrying around a reference to the S-Lang variable within Perl. The S-Lang variable is saved in an associative array within the _inline namespace, to ensure that it is not destroyed, and this is used when the Perl variable is sent to a S-Lang function. The "native" S-Lang types included in this list are: Any_Type, Ref_Type, BString_Type, File_Type, and FD_Type. It would be useful if we could handle these directly, but it is not easy (for instance I believe it would be hard to use the file handles with PerlIO code since PerlIO makes assumptions about who has opened the file).

    The conversion between S-Lang and PDL types - e.g. what type of piddle should an Int_Type array be converted to - has only been tested on 32-bit machines. It may well fail on 64 bit machines.

  3. The information - including the S-Lang source code - is written to a configuration file by Inline. The default location for the file is within the _Inline/ directory, but this can be changed, as discussed in the Inline documentation.

If the code has previously been run then this information can just be read from the configuration file. The information - whether read from file or calculated directly - is used to:

  • Set up the bindings between Perl and S-Lang functions.

  • Set up the Perl classes to handle the various datatypes that can not be processed natively within Perl.

  • Set up the functions and variables in the _inline namespace (these should never be accessed by user code).

Once the S-Lang code has been processed the Perl code is run.

CALLING A S-LANG FUNCTION

For each S-Lang function we want to bind to Perl, a small Perl subroutine is created which pushes the S-Lang function name (including any necessary namespace) onto the start of the stack and then calls the Inline::SLang::sl_call_function() subroutine (which is defined in Slang.xs and should never be called directly). By including the S-Lang function name on the stack we allow the Perl and S-Lang names to be different, as required by the BIND_SLFUNCS and BIND_NS configuration options.

The sl_call_function() routine performs 4 operations:

  1. The S-Lang interpreter is checked to see if the routine is defined (this step could be removed).

  2. The remaining items on the Perl stack (the arguments to the function) are converted to S-Lang variables and pushed onto the S-Lang stack. The conversion from Perl to S-Lang types is done by the pl2sl() routine which is defined in pl2sl.c.

  3. The S-Lang function is called. The S-Lang error hook is used to catch any errors that the S-Lang interpreter may throw; these are converted to Perl errors by calling the croak() function so that they can be trapped by Perl's eval() routine.

  4. The S-Lang stack is examined and any variables returned by the function are popped off, converted to Perl variables, and pushed onto the Perl stack. The order of the S-Lang stack is reversed when creating the Perl stack so that the S-Lang statement

      ( x, y ) = some_func();

    can be converted to

      ( my $x, my $y ) = some_func();

    The conversion from S-Lang to Perl types is done by the sl2pl() routine which is defined in sl2pl.c.

The major part of the whole module is the conversion between Perl and S-Lang variables, namely the pl2sl() and sl2pl() routines. The routines are not pretty - they could be re-written to allow additional convertors to be "plugged in" - but they seem to work.

NOTES

How to find out what S-Lang functions and datatypes are recognised

The info option of Inline can be used to find out what functions and data types are bound to Perl. If the code is stored in the file script.pl, use

  perl -MInline=info script.pl

Further details are available in the Inline::SLang::Config documentation.

Execution of S-Lang code

The S-Lang code is executed before any Perl code. This can be seen in the following example (available as examples/order.pl in the source distribution):

 use Inline 'SLang' => 'message("This is S-Lang");';
 print "This is Perl\n";

When run, the script displays:

 This is S-Lang
 This is Perl

Exporting symbols

The system used to bind functions to Perl - in particular to make the symbols available to Perl - is a "simplified" version of the Exporter Perl module (since I have been unable to successfully use Exporter directly).

SEE ALSO

Inline::SLang, Inline