Dico |
|
GNU Dictionary Server |
Sergey Poznyakoff |
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
, andinit-fun
. Their meaning is the same as forload-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 correspondingdatabase
block (see Databases). Optional argument args is a list of command line parameters obtained from cmdline inhandler
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 argumentdbh
is a database handle returned byopen-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 fromdescription
statement is used (see description). Otherwise, if nodescription
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 frominfo
statement is used (see info). If there is noinfo
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: Itscar
is a list of source languages, and itscdr
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
ormatch-word
callback.The data must be output to the current output port, e.g. using
display
orformat
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 theresult-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
car
s 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.
This document was generated on September 4, 2020 using makeinfo.
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.