Previous: Dico Scheme Primitives, Up: Guile
[Contents][Index]
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.
Previous: Dico Scheme Primitives, Up: Guile
[Contents][Index]