GNU Dico Manual (split by section):   Section:   Chapter:FastBack: Modules   Up: Modules   FastForward: Interface   Contents: Table of ContentsIndex: Concept Index

5.5 Guile

Guile is an acronym for GNU’s Ubiquitous Intelligent Language for Extensions. It provides a Scheme interpreter conforming to the R5RS language specification and a number of convenience functions. For information about the language, refer to Revised(5) Report on the Algorithmic Language Scheme. For a detailed description of Guile and its features, see Overview in The Guile Reference Manual.

The guile module provides an interface to Guile that allows for writing GNU Dico modules in Scheme. The module is loaded using the following configuration file statement:

load-module mod-name {
   command "guile [options]"
           " init-script=script"
           " init-args=args"
           " init-fun=function";
}

The init-script parameter specifies the name of a Scheme source file to be loaded in order to initialize the module. The init-args parameter supplies additional arguments to the module. They will be accessible to the script via command-line function. This parameter is optional.

The init-fun parameter specifies the name of a function that will be invoked to perform initialization of the module and of particular databases. See Guile Initialization, for a description of initialization sequence. Optional arguments, options, are:

debug

Enable Guile debugging and stack traces.

nodebug

Disable Guile debugging and stack traces (default).

load-path=path

Append directories from path to the list of directories which should be searched for Scheme modules and libraries. The path must be a list of directory names, separated by colons.

This option modifies the value of Guile’s %load-path variable. See the section Configuration and Installation in the Guile Reference Manual.

Guile databases are declared using the following syntax:

database {
        name "dbname";
        handler "mod-name [options] cmdline";
}

where:

dbname

gives the name for this database,

mod-name

the name given to Guile module in load-module statement (see above),

options

options that override global settings given in the load-module statement. The following options are understood: init-script, init-args, and init-fun. Their meaning is the same as for load-module statement (see above), except that they affect only this particular database.

cmdline

the command line that will be passed to the Guile open-db callback function (see open-db).

5.5.1 Virtual Functions

A database handled by the guile module is assigned a virtual function table. This table is an association list which keeps Scheme call-back functions implemented to perform particular tasks on that database. In this list, the car of each element contains the name of a function, and its cdr gives the corresponding function. The defined function names and their semantics are:

open

Open the database.

close

Close the database.

descr

Return a short description of the database.

info

Return a full information about the database.

define

Define a word.

match

Look up a word in the database.

output

Output a search result.

result-count

Return number of entries in the result.

For example, the following is a valid virtual function table:

(list (cons "open" open-module)
      (cons "close" close-module)
      (cons "descr" descr)
      (cons "info" info)
      (cons "define" define-word)
      (cons "match" match-word)
      (cons "output" output)
      (cons "result-count" result-count))

Apart from a per-database virtual table, there is also a global virtual function table, which supplies entries missing in the former. Both tables are created during the module initialization, as described in the next subsection.

The purposes of particular virtuals functions are described in Guile API.

5.5.2 Guile Initialization

The following configuration statement causes loading and initialization of the guile module:

load-module mod-name {
   command "guile init-script=script"
           " init-fun=function";
}

Upon module initialization stage, the module attempts to load the file named script. The file is loaded using primitive-load call (see Loading in The Guile Reference Manual), i.e. the load paths are not searched, so script must be an absolute path name. The init-fun parameter supplies the name of the initialization function. This Scheme function constructs virtual function tables for the module itself and for each database that uses this module. It must be declared as follows:

(define (function arg)
  ...)

This function is called several times. First of all, it is called after the script is loaded. This time it is given #f as its argument, and its return value is saved as a global function table. Then, it is called for each database statement that has mod-name (used in load-module above) in its handler keyword, e.g.:

database {
   name db-name;
   handler "mod-name …";
}

This time, it is given db-name as its argument and the value it returns is stored as the virtual function table for this particular database.

The following example function returns a complete virtual function table:

(define-public (my-dico-init arg)
  (list (cons "open" open-module)
        (cons "close" close-module)
        (cons "descr" descr)
        (cons "info" info)
        (cons "lang" lang)
        (cons "define" define-word)
        (cons "match" match-word)
        (cons "output" output)
        (cons "result-count" result-count)))

5.5.3 Guile API

This subsection describes callback functions that a Guile database module must provide. Each description begins with the function prototype and its entry in the virtual function table.

Callback functions can be subdivided into two groups: database functions and search functions.

Database callback functions are responsible for opening and closing databases and for returning information about them.

Guile Callback: open-db name . args

Virtual table: (cons "open" open-db)

Open the database. The argument name contains database name as given in the name statement of the corresponding database block (see Databases). Optional argument args is a list of command line parameters obtained from cmdline in handler statement (see guile-cmdline). For example, if the configuration file contained:

database {
    name "foo";
    handler "guile db=file 1 no";
}

then the open-db callback will be called as:

(open-db "foo" '("db=file" "1" "no"))

The open-db callback returns a database handle, i.e. an opaque object that will subsequently be used to identify this database. This value, hereinafter named dbh, will be passed to another callback functions that need to access the database.

The return value #f or '() indicates an error.

Guile Callback: close-db dbh

Virtual Table: (cons "close" close-db)

Close the database. This function is called during the cleanup procedure, before termination of dicod. The argument dbh is a database handle returned by open-db.

The return value from close-db is ignored. To communicate errors to the daemon, throw an exception.

Guile Callback: descr dbh

Virtual Table: (cons "descr" descr)

Return a short textual description of the database, for use in SHOW DB output. If there is no description, returns #f or '().

The argument dbh is a database handle returned by open-db.

This callback is optional. If it is not defined, or if it returns #f ('()), the text from description statement is used (see description). Otherwise, if no description statement is present, an empty string will be returned.

Guile Callback: info dbh

Virtual Table: (cons "info" info)

Return a verbose, eventually multi-line, textual description of the database, for use in SHOW INFO output. If there is no description, returns #f or '().

The argument dbh is a database handle returned by open-db.

This callback is optional. If it is not defined, or if it returns #f ('()), the text from info statement is used (see info). If there is no info statement, the string ‘No information available’ is used.

Guile Callback: lang dbh

Virtual Table: (cons "lang" lang)

Return a cons of languages supported by this database: Its car is a list of source languages, and its cdr is a list of destination languages. For example, the following return value indicates that the database contains translations from English to French and Spanish:

 (cons (list "en") (list "fr" "es"))

A database is searched in a two-phase process. First, an appropriate callback is called to do the search: define-word is called for DEFINE searches and match-word is called for matches. This callback returns an opaque entity, called result handle, which is then passed to the output callback, which is responsible for outputting it.

Guile Callback: define-word dbh word

Virtual Table: (cons "define" define-word)

Find definitions of word word in the database dbh. Return a result handle. If nothing is found, return #f or '().

The argument dbh is the database handle returned by open-db.

Guile Callback: match-word dbh strat key

Virtual Table: (cons "match" match-word)

Find in the database dbh all headwords that match key, using strategy strat. Return a result handle. If nothing is found, return #f or '().

The key is a Dico Key object, which contains information about the word being looked for. To obtain the actual word, use the dico-key->word function (see dico-key->word).

The argument dbh is a database handle returned by open-db. The matching strategy strat is a special Scheme object that can be accessed using a set of functions described below (see Dico Scheme Primitives).

Guile Callback: result-count resh

Virtual Table: (cons "result-count" result-count)

Return the number of elements in the result set resh.

Guile Callback: output resh n

Virtual Table: (cons "output" output)

Output nth result from the result set resh. The argument resh is a result handle returned by define-word or match-word callback.

The data must be output to the current output port, e.g. using display or format primitives. If resh represents a match result, the output must not be quoted or terminated by newlines.

It is guaranteed that the output callback will be called as many times as there are elements in resh (as determined by the result-count callback) and that for each subsequent call the value of n equals its value from the previous call incremented by one.

At the first call n equals 0.

5.5.4 Dico Scheme Primitives

GNU Dico provides the following Scheme primitives for accessing various fields of the strat and key arguments to match callback:

Function: dico-key? obj

Return ‘#t’ if obj is a Dico key object.

Function: dico-key->word key

Extract the lookup word from the key object key.

Function: dico-make-key strat word

Create new key object from strategy strat and word word.

Function: dico-strat-selector? strat

Return true if strat has a selector (see Selector).

Function: dico-strat-select? strat word key

Return true if key matches word as per strategy selector strat. The key is a ‘Dico Key’ object.

Function: dico-strat-name strat

Return the name of strategy strat.

Function: dico-strat-description strat

Return a textual description of the strategy strat.

Function: dico-strat-default? strat

Return true if strat is a default strategy. See default strategy.

Function: dico-register-strat strat descr [fun]

Register a new strategy. If fun is given it will be used as a callback for that strategy. Notice, that you can use strategies implemented in Guile in your C code as well (see strategy).

The selector function must be declared as follows:

(define (fun key word)
  ...)

It must return #t if key matches word, and #f otherwise.

5.5.5 Example Module

In this subsection we will show how to build a simple dicod module written in Scheme. The source code of this module, called listdict.scm and a short database for it, numerals-pl.db, are shipped with the distribution in the directory examples.

The database is stored in a disk file in form of a list. The first two elements of this list contain database description and full information strings. Rest of elements are conses, whose car contains the headword, and cdr contains the corresponding dictionary article. Following is an example of such a database:

("Short English-Norwegian numerals dictionary"
 "Short English-Norwegian dictionary of numerals (1 - 7)"
 ("one" . "en")
 ("two" . "to")
 ("three" . "tre")
 ("four" . "fire")
 ("five" . "fem")
 ("six" . "seks")
 ("seven" . "sju"))

We wish to declare such databases in dicod.conf the following way:

database {
        name "numerals";
        handler "guile example.db";
}

Thus, the rest argument to ‘open-db’ callback will be ‘("guile" "example.db")’ (see open-db). Given this, we may write the callback as follows:

(define (open-db name . rest)
  (let ((db (with-input-from-file
                (cadr rest)
              (lambda () (read)))))
    (cond
     ((list? db) (cons name db))
     (else
      (format (current-error-port) "open-module: ~A: invalid format\n"
              (car args))
      #f))))

The list returned by this callback will then be passed as a database handle to another callback functions. To facilitate access to particular elements of this list, it is convenient to define the following syntax:

(define-syntax db:get
  (syntax-rules (info descr name corpus)
    ((db:get dbh name)   ;; Return the name of the database.
     (list-ref dbh 0))
    ((db:get dbh descr)  ;; Return the desctiption.
     (list-ref dbh 1))
    ((db:get dbh info)   ;; Return the info string. 
     (list-ref dbh 2))
    ((db:get dbh corpus) ;; Return the word list.
     (list-tail dbh 3))))

Now, we can write ‘descr’ and ‘info’ callbacks:

(define (descr dbh)
  (db:get dbh descr))

(define (info dbh)
  (db:get dbh info))

The two callbacks ‘define-word’ and ‘match-word’ provide the core module functionality. Their results will be passed to ‘output’ and ‘result-count’ callbacks as a “result handler” argument. In the spirit of Scheme, we make the result a list. Its car is a boolean value: #t, if the result comes from ‘define-word’ callback, and #f if it comes from ‘match-word’. The cdr of this list contains a list of matches. For ‘define-word’, it is a list of conses copied from the database word list, whereas for ‘match-word’, it is a list of headwords.

The ‘define-word’ callback returns all list entries whose cars contain the look up word. It uses mapcan function, which is supposed to be defined elsewhere:

(define (define-word dbh word)
  (let ((res (mapcan (lambda (elt)
                       (and (string-ci=? word (car elt))
                            elt))
                     (db:get dbh corpus))))
    (and res (cons #t res))))

The ‘match-word’ callback (see match-word) takes three arguments: a database handler dbh, a strategy descriptor strat, and a word word to look for. The result handle it returns contains a list of headwords from the database that match word in the sense of strat. Thus, the behavior of ‘match-word’ depends on the strat. To implement this, let’s define a list of directly supported strategies (see below for definitions of particular ‘match-’ functions):

(define strategy-list
  (list (cons "exact"  match-exact)
        (cons "prefix"  match-prefix)
        (cons "suffix"  match-suffix)))

The ‘match-word’ callback will then select an entry from that list and call its cdr, e.g.:

(define (match-word dbh strat key)
  (let ((sp (assoc (dico-strat-name strat) strategy-list)))
    (let ((res (cond
                (sp
                 ((cdr sp) dbh strat (dico-key->word key)))

If the requested strategy is not in that list, the function will use the selector function if it is available, and the default matching function otherwise:

                ((dico-strat-selector? strat)
                 (match-selector dbh strat key))
                (else
                 (match-default dbh strat (dico-key->word key))))))

Notice the use of dico-key->word function to extract the actual lookup word from the key object.

To summarize, the ‘match-word’ callback is:

(define (match-word dbh strat key)
  (let ((sp (assoc (dico-strat-name strat) strategy-list)))
    (let ((res (cond
                (sp
                 ((cdr sp) dbh strat (dico-key->word key)))
                ((dico-strat-selector? strat)
                 (match-selector dbh strat key))
                (else
                 (match-default dbh strat (dico-key->word key))))))
      (if res
          (cons #f res)
          #f))))

Now, let’s create the ‘match-’ functions it uses. The ‘exact’ strategy is easy to implement:

(define (match-exact dbh strat word)
  (mapcan (lambda (elt)
            (and (string-ci=? word (car elt))
                 (car elt)))
          (db:get dbh corpus)))

The ‘prefix’ and ‘suffix’ strategies are implemented using SRFI-13 (see SRFI-13 in The Guile Reference Manual) functions string-prefix-ci? and string-suffix-ci?, e.g.:

(define (match-prefix dbh strat word)
  (mapcan (lambda (elt)
            (and (string-prefix-ci? word (car elt))
                 (car elt)))
          (db:get dbh corpus)))

Notice that whereas the ‘prefix’ strategy is defined by the server itself, the ‘suffix’ strategy is an extension, and should therefore be registered:

(dico-register-strat "suffix" "Match word suffixes")

The match-selector function is pretty similar to its siblings, except that it uses dico-strat-select? (see dico-strat-select?) to select the matching elements. This also leads to this function expecting a key as its third argument, in contrast to the previous matchers, which expect the actual lookup word there:

(define (match-selector dbh strat key)
  (mapcan (lambda (elt)
            (and (dico-strat-select? strat (car elt) key)
                 (car elt)))
          (db:get dbh corpus)))

Finally, the match-default is a variable that refers to the default matching strategy for this module, e.g.:

(define match-default match-prefix)

The two callbacks left to define are ‘result-count’ and ‘output’. The first of them simply returns the number of elements in cdr of the result:

(define (result-count rh)
  (length (cdr rh)))

The behavior of ‘output’ depends on whether the result is produced by ‘define-word’ or by ‘match-word’.

(define (output rh n)
  (if (car rh)
      ;; Result comes from DEFINE command.
      (let ((res (list-ref (cdr rh) n)))
        (display (car res))
        (newline)
        (display (cdr res)))
      ;; Result comes from MATCH command.
      (display (list-ref (cdr rh) n))))

Finally, at the end of the module the callbacks are made known to dicod by the module initialization function:

(define-public (example-init arg)
  (list (cons "open" open-module)
        (cons "descr" descr)
        (cons "info" info)
        (cons "define" define-word)
        (cons "match" match-word)
        (cons "output" output)
        (cons "result-count" result-count)))

Notice, that in this implementation ‘close-db’ callback was not needed.

GNU Dico Manual (split by section):   Section:   Chapter:FastBack: Modules   Up: guile   FastForward: Interface   Contents: Table of ContentsIndex: Concept Index