A Concrete Example

In order to get a feel for using Melange, it is probably best to start with a concrete example. This section contains a complete program which will use native C libraries to list the contents of some directories. For now, you should simply skim this example to get a general overview of Melange's capabilities. These will be described in more detail in later sections.

We will first begin with an "interface file" which contains a mixture of basic Dylan code and "define interface" forms which will be processed by Melange. We will name this file "dirent.intr".

module: Junk
synopsis: A poor imitation of "ls"

define library junk
  use dylan;
  use streams;
end library junk;

define module junk
  use dylan;
  use extensions;
  use extern;
  use streams;
  use standard-io;
end module junk;

define interface
  // This clause is more complex than it needs to be, but it does
  // demonstrate a lot of Melange's features.
  #include "/usr/include/sys/dirent.h",
    mindy-include-file: "dirent.inc",
    equate: {"char /* Any C declaration is legal */ *" => <c-string>},
    map: {"char *" => <byte-string>},
    // The two functions require callbacks, which we don’t support.
    exclude: {"scandir", "alphasort", "struct _dirdesc"},
    seal-functions: open,
    read-only: #t,
    name-mapper: minimal-name-mapping;
  function "opendir", map-argument: {#x1 => <string>};
  function "telldir" => tell, map-result: <integer>;
  struct "struct dirent",
    prefix: "dt-",
    exclude: {"d_namlen", "d_reclen"};
end interface;

define method main (program, #rest args)
  for (arg in args)
    let dir = opendir(arg);
    for (entry = readdir(dir) then readdir(dir),
         until entry = $null-pointer)
      write-line(entry.dt-d-name, *standard-output*);
    end for;
    closedir(dir);
  end for;
end method main;

We will then process this file through Melange to produce a file of pure Dylan code. On a system with d2c support, there will be an executable named melange, which you can invoke like this:

      melange dirent.intr dirent.dylan
      
On a mindy-only system, where melange is contained in a file melange.dbc, we would use the following command line:

      mindy -f melange.dbc dirent.intr dirent.dylan
      

This command will process "melange.intr" and write a file named "dirent.dylan". In this case, it will also silently write a file named "dirent.inc", whose use will be explained later.

You can compile "dirent.dylan" normally, via mindycomp, but in order to execute it, you must make sure that the Mindy interpreter will be able to load the appropriate routines from the library containing the "dirent" routines. Ideally, you would simply let Mindy load the appropriate code dynamically. However, this is presently only available for a few machines. Therefore, we will follow a messier approach and build a new version of the interpreter which is aware of the desired functions.

Move to the build directory for the Mindy interpreter, and edit the Makefile so the "EXTERN-INCLUDES" line mentions "your_dir_path/dirent.inc" and then run "make mindy". In this case, this is all that is required to build a new interpreter which is aware of the dirent routines.

You can now put it all together, invoking the new interpreter on the compiled program, with:

        mindy -f dirent.dbc .
      

This should print a list of all files in the current directory.

Because of the difficulty of relinking the interpreter for each new library, it is expected that administrators will build a set of "standard" library interfaces which are prelinked into the interpreter and exported as general Dylan library interfaces. In the future, as Melange (and the Gwydion environment) are extended to support better linking and loading capabilities, it should become easier to incorporate C libraries on an "as-needed" basis.