6. Database Packages
6.1 Relational Database
(require 'relational-database)
This package implements a database system inspired by the Relational
Model (E. F. Codd, A Relational Model of Data for Large Shared
Data Banks). An SLIB relational database implementation can be created
from any 6.2.1 Base Table implementation.
Why relational database? For motivations and design issues see
http://swissnet.ai.mit.edu/~jaffer/DBManifesto.html.
6.1.1 Using Databases
(require 'databases)
This enhancement wraps a utility layer on relational-database
which provides:
-
Identification of open databases by filename.
-
Automatic sharing of open (immutable) databases.
-
Automatic loading of base-table package when creating a database.
-
Detection and automatic loading of the appropriate base-table package
when opening a database.
-
Table and data definition from Scheme lists.
Database Sharing
Auto-sharing refers to a call to the procedure
open-database returning an already open database (procedure),
rather than opening the database file a second time.
Note: Databases returned by open-database do not include
wrappers applied by packages like 6.1.4 Embedded Commands. But
wrapped databases do work as arguments to these functions.
When a database is created, it is mutable by the creator and not
auto-sharable. A database opened mutably is also not auto-sharable.
But any number of readers can (open) share a non-mutable database file.
This next set of procedures mirror the whole-database methods in
6.2.4 Database Operations. Except for create-database, each
procedure will accept either a filename or database procedure for its
first argument.
- Function: create-database filename base-table-type
filename should be a string naming a file; or #f. base-table-type must be a
symbol naming a feature which can be passed to require. create-database
returns a new, open relational database (with base-table type base-table-type)
associated with filename, or a new ephemeral database if filename is #f.
create-database is the only run-time use of require in SLIB
which crosses module boundaries. When base-table-type is required by create-database; it
adds an association of base-table-type with its relational-system procedure
to mdbm:*databases*.
alist-table is the default base-table type:
| | (require 'databases)
(define my-rdb (create-database "my.db" 'alist-table))
|
Only alist-table and base-table modules which have been
required will dispatch correctly from the
open-database procedures. Therefore, either pass two
arguments to open-database, or require the base-table of your
database file uses before calling open-database with one
argument.
- Procedure: open-database! rdb base-table-type
Returns mutable open relational database or #f.
- Function: open-database rdb base-table-type
Returns an open relational database associated with rdb. The
database will be opened with base-table type base-table-type).
- Function: open-database rdb
- Returns an open relational database associated with rdb.
open-database will attempt to deduce the correct base-table-type.
- Function: write-database rdb filename
Writes the mutable relational-database rdb to filename.
- Function: sync-database rdb
Writes the mutable relational-database rdb to the filename it was
opened with.
- Function: solidify-database rdb
Syncs rdb and makes it immutable.
- Function: close-database rdb
rdb will only be closed when the count of open-database - close-database
calls for rdb (and its filename) is 0. close-database returns #t if successful;
and #f otherwise.
- Function: mdbm:report
Prints a table of open database files. The columns are the
base-table type, number of opens, `!' for mutable, the
filename, and the lock certificate (if locked).
| | (mdbm:report)
-|
alist-table 003 /usr/local/lib/slib/clrnamdb.scm
alist-table 001 ! sdram.db jaffer@aubrey.jaffer.3166:1038628199
|
Opening Tables
- Function: open-table rdb table-name
rdb must be a relational database and table-name a symbol.
open-table returns a "methods" procedure for an existing relational table in
rdb if it exists and can be opened for reading, otherwise returns
#f.
- Procedure: open-table! rdb table-name
rdb must be a relational database and table-name a symbol.
open-table! returns a "methods" procedure for an existing relational table in
rdb if it exists and can be opened in mutable mode, otherwise returns
#f.
Defining Tables
- Function: define-domains rdb row5 ...
Adds the domain rows row5 ... to the `*domains-data*' table
in rdb. The format of the row is given in 6.2.2 Catalog Representation.
| | (define-domains rdb '(permittivity #f complex? c64 #f))
|
- Function: add-domain rdb row5
Use define-domains instead.
- Function: define-tables rdb spec-0 ...
Adds tables as specified in spec-0 ... to the open
relational-database rdb. Each spec has the form:
| | (<name> <descriptor-name> <descriptor-name> <rows>)
|
or
| | (<name> <primary-key-fields> <other-fields> <rows>)
|
where <name> is the table name, <descriptor-name> is the symbol
name of a descriptor table, <primary-key-fields> and
<other-fields> describe the primary keys and other fields
respectively, and <rows> is a list of data rows to be added to the
table.
<primary-key-fields> and <other-fields> are lists of field
descriptors of the form:
or
| | (<column-name> <domain> <column-integrity-rule>)
|
where <column-name> is the column name, <domain> is the domain
of the column, and <column-integrity-rule> is an expression whose
value is a procedure of one argument (which returns #f to signal
an error).
If <domain> is not a defined domain name and it matches the name of
this table or an already defined (in one of spec-0 ...) single
key field table, a foreign-key domain will be created for it.
Listing Tables
- Function: list-table-definition rdb table-name
If symbol table-name exists in the open relational-database
rdb, then returns a list of the table-name, its primary key names
and domains, its other key names and domains, and the table's records
(as lists). Otherwise, returns #f.
The list returned by list-table-definition, when passed as an
argument to define-tables, will recreate the table.
6.1.2 Table Operations
These are the descriptions of the methods available from an open
relational table. A method is retrieved from a table by calling
the table with the symbol name of the operation. For example:
| | ((plat 'get 'processor) 'djgpp) => i386
|
Some operations described below require primary key arguments. Primary
keys arguments are denoted key1 key2 .... It is an
error to call an operation for a table which takes primary key arguments
with the wrong number of primary keys for that table.
- Operation: relational-table get column-name
- Returns a procedure of arguments key1 key2 ... which
returns the value for the column-name column of the row associated
with primary keys key1, key2 ... if that row exists in
the table, or
#f otherwise.
| | ((plat 'get 'processor) 'djgpp) => i386
((plat 'get 'processor) 'be-os) => #f
|
6.1.2.1 Single Row Operations
The term row used below refers to a Scheme list of values (one for
each column) in the order specified in the descriptor (table) for this
table. Missing values appear as #f. Primary keys must not
be missing.
- Operation: relational-table row:insert
- Adds the row row to this table. If a row for the primary key(s)
specified by row already exists in this table an error is
signaled. The value returned is unspecified.
| | (define telephone-table-desc
((my-database 'create-table) 'telephone-table-desc))
(define ndrp (telephone-table-desc 'row:insert))
(ndrp '(1 #t name #f string))
(ndrp '(2 #f telephone
(lambda (d)
(and (string? d) (> (string-length d) 2)
(every
(lambda (c)
(memv c '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
#\+ #\( #\ #\) #\-)))
(string->list d))))
string))
|
- Operation: relational-table row:update
- Returns a procedure of one argument, row, which adds the row,
row, to this table. If a row for the primary key(s) specified by
row already exists in this table, it will be overwritten. The
value returned is unspecified.
- Operation: relational-table row:retrieve
- Returns a procedure of arguments key1 key2 ... which
returns the row associated with primary keys key1, key2
... if it exists, or
#f otherwise.
| | ((plat 'row:retrieve) 'linux) => (linux i386 linux gcc)
((plat 'row:retrieve) 'multics) => #f
|
- Operation: relational-table row:remove
- Returns a procedure of arguments key1 key2 ... which
removes and returns the row associated with primary keys key1,
key2 ... if it exists, or
#f otherwise.
- Operation: relational-table row:delete
- Returns a procedure of arguments key1 key2 ... which
deletes the row associated with primary keys key1, key2
... if it exists. The value returned is unspecified.
6.1.2.2 Match-Keys
The (optional) match-key1 ... arguments are used to restrict
actions of a whole-table operation to a subset of that table. Those
procedures (returned by methods) which accept match-key arguments will
accept any number of match-key arguments between zero and the number of
primary keys in the table. Any unspecified match-key arguments
default to #f.
The match-key1 ... restrict the actions of the table command
to those records whose primary keys each satisfy the corresponding
match-key argument. The arguments and their actions are:
#f
- The false value matches any key in the corresponding position.
- an object of type procedure
- This procedure must take a single argument, the key in the corresponding
position. Any key for which the procedure returns a non-false value is
a match; Any key for which the procedure returns a
#f is not.
- other values
- Any other value matches only those keys
equal? to it.
- Operation: relational-table get* column-name
- Returns a procedure of optional arguments match-key1 ... which
returns a list of the values for the specified column for all rows in
this table. The optional match-key1 ... arguments restrict
actions to a subset of the table.
| | ((plat 'get* 'processor)) =>
(i386 i8086 i386 i8086 i386 i386 i8086 m68000
m68000 m68000 m68000 m68000 powerpc)
((plat 'get* 'processor) #f) =>
(i386 i8086 i386 i8086 i386 i386 i8086 m68000
m68000 m68000 m68000 m68000 powerpc)
(define (a-key? key)
(char=? #\a (string-ref (symbol->string key) 0)))
((plat 'get* 'processor) a-key?) =>
(m68000 m68000 m68000 m68000 m68000 powerpc)
((plat 'get* 'name) a-key?) =>
(atari-st-turbo-c atari-st-gcc amiga-sas/c-5.10
amiga-aztec amiga-dice-c aix)
|
6.1.2.3 Multi-Row Operations
- Operation: relational-table row:retrieve*
- Returns a procedure of optional arguments match-key1 ...
which returns a list of all rows in this table. The optional
match-key1 ... arguments restrict actions to a subset of the
table. For details see See section 6.1.2.2 Match-Keys.
| | ((plat 'row:retrieve*) a-key?) =>
((atari-st-turbo-c m68000 atari turbo-c)
(atari-st-gcc m68000 atari gcc)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(amiga-aztec m68000 amiga aztec)
(amiga-dice-c m68000 amiga dice-c)
(aix powerpc aix -))
|
- Operation: relational-table row:remove*
- Returns a procedure of optional arguments match-key1 ... which
removes and returns a list of all rows in this table. The optional
match-key1 ... arguments restrict actions to a subset of the
table.
- Operation: relational-table row:delete*
- Returns a procedure of optional arguments match-key1 ...
which Deletes all rows from this table. The optional match-key1
... arguments restrict deletions to a subset of the table. The
value returned is unspecified. The descriptor table and catalog entry
for this table are not affected.
- Operation: relational-table for-each-row
- Returns a procedure of arguments proc match-key1 ...
which calls proc with each row in this table. The
optional match-key1 ... arguments restrict actions to a
subset of the table. For details see See section 6.1.2.2 Match-Keys.
Note that row:insert* and row:update* do not use
match-keys.
- Operation: relational-table row:insert*
- Returns a procedure of one argument, rows, which adds each row in
the list of rows, rows, to this table. If a row for the primary
key specified by an element of rows already exists in this table,
an error is signaled. The value returned is unspecified.
- Operation: relational-table row:update*
- Returns a procedure of one argument, rows, which adds each row in
the list of rows, rows, to this table. If a row for the primary
key specified by an element of rows already exists in this table,
it will be overwritten. The value returned is unspecified.
6.1.2.4 Indexed Sequential Access Methods
Indexed Sequential Access Methods are a way of arranging
database information so that records can be accessed both by key and
by key sequence (ordering). ISAM is not part of Codd's
relational model. Hardcore relational programmers might use some
least-upper-bound join for every row to get them into an order.
Associative memory in B-Trees is an example of a database
implementation which can support a native key ordering. SLIB's
alist-table implementation uses sort to implement
for-each-row-in-order, but does not support isam-next
and isam-prev.
The multi-primary-key ordering employed by these operations is the
lexicographic collation of those primary-key fields in their given
order. For example:
| | (12 a 34) < (12 a 36) < (12 b 1) < (13 a 0)
|
6.1.2.5 Sequential Index Operations
The following procedures are individually optional depending on the
base-table implememtation. If an operation is not supported,
then calling the table with that operation symbol will return false.
- Operation: relational-table for-each-row-in-order
- Returns a procedure of arguments proc match-key1 ...
which calls proc with each row in this table in the
(implementation-dependent) natural, repeatable ordering for rows. The
optional match-key1 ... arguments restrict actions to a
subset of the table. For details see See section 6.1.2.2 Match-Keys.
- Operation: relational-table isam-next
- Returns a procedure of arguments key1 key2 ... which
returns the key-list identifying the lowest record higher than
key1 key2 ... which is stored in the relational-table;
or false if no higher record is present.
- Operation: relational-table isam-next column-name
- The symbol column-name names a key field. In the list returned
by
isam-next, that field, or a field to its left, will be
changed. This allows one to skip over less significant key fields.
- Operation: relational-table isam-prev
- Returns a procedure of arguments key1 key2 ... which
returns the key-list identifying the highest record less than
key1 key2 ... which is stored in the relational-table;
or false if no lower record is present.
- Operation: relational-table isam-prev index
- The symbol column-name names a key field. In the list returned
by
isam-next, that field, or a field to its left, will be
changed. This allows one to skip over less significant key fields.
For example, if a table has key fields:
| | (col1 col2)
(9 5)
(9 6)
(9 7)
(9 8)
(12 5)
(12 6)
(12 7)
|
Then:
| | ((table 'isam-next) '(9 5)) => (9 6)
((table 'isam-next 'col2) '(9 5)) => (9 6)
((table 'isam-next 'col1) '(9 5)) => (12 5)
((table 'isam-prev) '(12 7)) => (12 6)
((table 'isam-prev 'col2) '(12 7)) => (12 6)
((table 'isam-prev 'col1) '(12 7)) => (9 8)
|
6.1.2.6 Table Administration
- Operation: relational-table column-names
-
- Operation: relational-table column-foreigns
-
- Operation: relational-table column-domains
-
- Operation: relational-table column-types
- Return a list of the column names, foreign-key table names, domain
names, or type names respectively for this table. These 4 methods are
different from the others in that the list is returned, rather than a
procedure to obtain the list.
- Operation: relational-table primary-limit
- Returns the number of primary keys fields in the relations in this
table.
- Operation: relational-table close-table
- Subsequent operations to this table will signal an error.
6.1.3 Database Interpolation
(require 'database-interpolate)
Indexed sequential access methods allow finding the keys (having
associations) closest to a given value. This facilitates the
interpolation of associations between those in the table.
- Function: interpolate-from-table table column
- Table should be a relational table with one numeric primary key
field which supports the
isam-prev and isam-next
operations. column should be a symbol or exact positive integer
designating a numerically valued column of table.
interpolate-from-table calculates and returns a value
proportionally intermediate between its values in the next and
previous key records contained in table. For keys larger than
all the stored keys the value associated with the largest stored key
is used. For keys smaller than all the stored keys the value
associated with the smallest stored key is used.
6.1.4 Embedded Commands
(require 'database-commands)
This enhancement wraps a utility layer on relational-database
which provides:
-
Automatic execution of initialization commands stored in database.
-
Transparent execution of database commands stored in
*commands*
table in database.
When an enhanced relational-database is called with a symbol which
matches a name in the *commands* table, the associated
procedure expression is evaluated and applied to the enhanced
relational-database. A procedure should then be returned which the user
can invoke on (optional) arguments.
The command *initialize* is special. If present in the
*commands* table, open-database or open-database!
will return the value of the *initialize* command. Notice that
arbitrary code can be run when the *initialize* procedure is
automatically applied to the enhanced relational-database.
Note also that if you wish to shadow or hide from the user
relational-database methods described in 6.2.4 Database Operations, this
can be done by a dispatch in the closure returned by the
*initialize* expression rather than by entries in the
*commands* table if it is desired that the underlying methods
remain accessible to code in the *commands* table.
6.1.4.1 Database Extension
- Function: wrap-command-interface rdb
- Returns relational database rdb wrapped with additional commands
defined in its *commands* table.
- Function: add-command-tables rdb
- The relational database rdb must be mutable.
add-command-tables adds a *command* table to rdb; then
returns
(wrap-command-interface rdb).
- Function: define-*commands* rdb spec-0 ...
Adds commands to the *commands* table as specified in
spec-0 ... to the open relational-database rdb. Each
spec has the form:
| | ((<name> <rdb>) "comment" <expression1> <expression2> ...)
|
or
| | ((<name> <rdb>) <expression1> <expression2> ...)
|
where <name> is the command name, <rdb> is a formal passed the
calling relational database, "comment" describes the
command, and <expression1>, <expression1>, ... are the
body of the procedure.
define-*commands* adds to the *commands* table a command
<name>:
| | (lambda (<name> <rdb>) <expression1> <expression2> ...)
|
- Function: open-command-database filename
-
- Function: open-command-database filename base-table-type
- Returns an open enhanced relational database associated with
filename. The database will be opened with base-table type
base-table-type) if supplied. If base-table-type is not
supplied,
open-command-database will attempt to deduce the correct
base-table-type. If the database can not be opened or if it lacks the
*commands* table, #f is returned.
- Function: open-command-database! filename
-
- Function: open-command-database! filename base-table-type
- Returns mutable open enhanced relational database ...
- Function: open-command-database database
- Returns database if it is an immutable relational database; #f
otherwise.
- Function: open-command-database! database
- Returns database if it is a mutable relational database; #f
otherwise.
6.1.4.2 Command Intrinsics
Some commands are defined in all extended relational-databases. The are
called just like 6.2.4 Database Operations.
- Operation: relational-database add-domain domain-row
- Adds domain-row to the domains table if there is no row in
the domains table associated with key
(car domain-row) and
returns #t. Otherwise returns #f.
For the fields and layout of the domain table, See section 6.2.2 Catalog Representation. Currently, these fields are
-
domain-name
-
foreign-table
-
domain-integrity-rule
-
type-id
-
type-param
The following example adds 3 domains to the `build' database.
`Optstring' is either a string or #f. filename is a
string and build-whats is a symbol.
| | (for-each (build 'add-domain)
'((optstring #f
(lambda (x) (or (not x) (string? x)))
string
#f)
(filename #f #f string #f)
(build-whats #f #f symbol #f)))
|
- Operation: relational-database delete-domain domain-name
- Removes and returns the domain-name row from the domains
table.
- Operation: relational-database domain-checker domain
- Returns a procedure to check an argument for conformance to domain
domain.
6.1.4.3 Define-tables Example
The following example shows a new database with the name of
`foo.db' being created with tables describing processor families
and processor/os/compiler combinations. The database is then
solidified; saved and changed to immutable.
| | (require 'databases)
(define my-rdb (create-database "foo.db" 'alist-table))
(define-tables my-rdb
'(processor-family
((family atom))
((also-ran processor-family))
((m68000 #f)
(m68030 m68000)
(i386 i8086)
(i8086 #f)
(powerpc #f)))
'(platform
((name symbol))
((processor processor-family)
(os symbol)
(compiler symbol))
((aix powerpc aix -)
(amiga-dice-c m68000 amiga dice-c)
(amiga-aztec m68000 amiga aztec)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(atari-st-gcc m68000 atari gcc)
(atari-st-turbo-c m68000 atari turbo-c)
(borland-c-3.1 i8086 ms-dos borland-c)
(djgpp i386 ms-dos gcc)
(linux i386 linux gcc)
(microsoft-c i8086 ms-dos microsoft-c)
(os/2-emx i386 os/2 gcc)
(turbo-c-2 i8086 ms-dos turbo-c)
(watcom-9.0 i386 ms-dos watcom))))
(solidify-database my-rdb)
|
6.1.4.4 The *commands* Table
The table *commands* in an enhanced relational-database has
the fields (with domains):
| | PRI name symbol
parameters parameter-list
procedure expression
documentation string
|
The parameters field is a foreign key (domain
parameter-list) of the *catalog-data* table and should
have the value of a table described by *parameter-columns*. This
parameter-list table describes the arguments suitable for passing
to the associated command. The intent of this table is to be of a form
such that different user-interfaces (for instance, pull-down menus or
plain-text queries) can operate from the same table. A
parameter-list table has the following fields:
| | PRI index ordinal
name symbol
arity parameter-arity
domain domain
defaulter expression
expander expression
documentation string
|
The arity field can take the values:
single
- Requires a single parameter of the specified domain.
optional
- A single parameter of the specified domain or zero parameters is
acceptable.
boolean
- A single boolean parameter or zero parameters (in which case
#f
is substituted) is acceptable.
nary
- Any number of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
nary1
- One or more of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
The domain field specifies the domain which a parameter or
parameters in the indexth field must satisfy.
The defaulter field is an expression whose value is either
#f or a procedure of one argument (the parameter-list) which
returns a list of the default value or values as appropriate.
Note that since the defaulter procedure is called every time a
default parameter is needed for this column, sticky defaults can
be implemented using shared state with the domain-integrity-rule.
6.1.4.5 Command Service
- Function: make-command-server rdb table-name
- Returns a procedure of 2 arguments, a (symbol) command and a call-back
procedure. When this returned procedure is called, it looks up
command in table table-name and calls the call-back
procedure with arguments:
- command
- The command
- command-value
- The result of evaluating the expression in the procedure field of
table-name and calling it with rdb.
- parameter-name
- A list of the official name of each parameter. Corresponds to the
name field of the command's parameter-table.
- positions
- A list of the positive integer index of each parameter. Corresponds to
the
index field of the command's parameter-table.
- arities
- A list of the arities of each parameter. Corresponds to the
arity field of the command's parameter-table. For a
description of arity see table above.
- types
- A list of the type name of each parameter. Correspnds to the
type-id field of the contents of the domain of the
command's parameter-table.
- defaulters
- A list of the defaulters for each parameter. Corresponds to
the
defaulters field of the command's parameter-table.
- domain-integrity-rules
- A list of procedures (one for each parameter) which tests whether a
value for a parameter is acceptable for that parameter. The procedure
should be called with each datum in the list for
nary arity
parameters.
- aliases
- A list of lists of
(alias parameter-name). There can be
more than one alias per parameter-name.
For information about parameters, See section 4.4.4 Parameter lists.
6.1.4.6 Command Example
Here is an example of setting up a command with arguments and parsing
those arguments from a getopt style argument list
(see section 4.4.1 Getopt).
| | (require 'database-commands)
(require 'databases)
(require 'getopt-parameters)
(require 'parameters)
(require 'getopt)
(require 'fluid-let)
(require 'printf)
(define my-rdb (add-command-tables (create-database #f 'alist-table)))
(define-tables my-rdb
'(foo-params
*parameter-columns*
*parameter-columns*
((1 single-string single string
(lambda (pl) '("str")) #f "single string")
(2 nary-symbols nary symbol
(lambda (pl) '()) #f "zero or more symbols")
(3 nary1-symbols nary1 symbol
(lambda (pl) '(symb)) #f "one or more symbols")
(4 optional-number optional ordinal
(lambda (pl) '()) #f "zero or one number")
(5 flag boolean boolean
(lambda (pl) '(#f)) #f "a boolean flag")))
'(foo-pnames
((name string))
((parameter-index ordinal))
(("s" 1)
("single-string" 1)
("n" 2)
("nary-symbols" 2)
("N" 3)
("nary1-symbols" 3)
("o" 4)
("optional-number" 4)
("f" 5)
("flag" 5)))
'(my-commands
((name symbol))
((parameters parameter-list)
(parameter-names parameter-name-translation)
(procedure expression)
(documentation string))
((foo
foo-params
foo-pnames
(lambda (rdb) (lambda args (print args)))
"test command arguments"))))
(define (dbutil:serve-command-line rdb command-table command argv)
(set! *argv* (if (vector? argv) (vector->list argv) argv))
((make-command-server rdb command-table)
command
(lambda (comname comval options positions
arities types defaulters dirs aliases)
(apply comval (getopt->arglist options positions
arities types defaulters dirs aliases)))))
(define (cmd . opts)
(fluid-let ((*optind* 1))
(printf "%-34s => "
(call-with-output-string
(lambda (pt) (write (cons 'cmd opts) pt))))
(set! opts (cons "cmd" opts))
(force-output)
(dbutil:serve-command-line
my-rdb 'my-commands 'foo (length opts) opts)))
(cmd) => ("str" () (symb) () #f)
(cmd "-f") => ("str" () (symb) () #t)
(cmd "--flag") => ("str" () (symb) () #t)
(cmd "-o177") => ("str" () (symb) (177) #f)
(cmd "-o" "177") => ("str" () (symb) (177) #f)
(cmd "--optional" "621") => ("str" () (symb) (621) #f)
(cmd "--optional=621") => ("str" () (symb) (621) #f)
(cmd "-s" "speciality") => ("speciality" () (symb) () #f)
(cmd "-sspeciality") => ("speciality" () (symb) () #f)
(cmd "--single" "serendipity") => ("serendipity" () (symb) () #f)
(cmd "--single=serendipity") => ("serendipity" () (symb) () #f)
(cmd "-n" "gravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "-ngravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "--nary" "chastity") => ("str" () (chastity) () #f)
(cmd "--nary=chastity" "") => ("str" () ( chastity) () #f)
(cmd "-N" "calamity") => ("str" () (calamity) () #f)
(cmd "-Ncalamity") => ("str" () (calamity) () #f)
(cmd "--nary1" "surety") => ("str" () (surety) () #f)
(cmd "--nary1=surety") => ("str" () (surety) () #f)
(cmd "-N" "levity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "-Nlevity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "--nary1=surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "-?")
-|
Usage: cmd [OPTION ARGUMENT ...] ...
-f, --flag
-o, --optional[=]<number>
-n, --nary[=]<symbols> ...
-N, --nary1[=]<symbols> ...
-s, --single[=]<string>
ERROR: getopt->parameter-list "unrecognized option" "-?"
|
6.1.5 Database Macros
(require 'within-database)
The object-oriented programming interface to SLIB relational databases
has failed to support clear, understandable, and modular code-writing
for database applications.
This seems to be a failure of the object-oriented paradigm where the
type of an object is not manifest (or even traceable) in source code.
within-database, along with the `databases' package,
reorganizes high-level database functions toward a more declarative
style. Using this package, one can tag database table and command
declarations for emacs:
| | etags -lscheme -r'/ *(define-\(command\5FT">[Contents] |
[Index] |
[ ? ] |
6.1.4.3 Define-tables Example
The following example shows a new database with the name of
`foo.db' being created with tables describing processor families
and processor/os/compiler combinations. The database is then
solidified; saved and changed to immutable.
| | (require 'databases)
(define my-rdb (create-database "foo.db" 'alist-table))
(define-tables my-rdb
'(processor-family
((family atom))
((also-ran processor-family))
((m68000 #f)
(m68030 m68000)
(i386 i8086)
(i8086 #f)
(powerpc #f)))
'(platform
((name symbol))
((processor processor-family)
(os symbol)
(compiler symbol))
((aix powerpc aix -)
(amiga-dice-c m68000 amiga dice-c)
(amiga-aztec m68000 amiga aztec)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(atari-st-gcc m68000 atari gcc)
(atari-st-turbo-c m68000 atari turbo-c)
(borland-c-3.1 i8086 ms-dos borland-c)
(djgpp i386 ms-dos gcc)
(linux i386 linux gcc)
(microsoft-c i8086 ms-dos microsoft-c)
(os/2-emx i386 os/2 gcc)
(turbo-c-2 i8086 ms-dos turbo-c)
(watcom-9.0 i386 ms-dos watcom))))
(solidify-database my-rdb)
|
6.1.4.4 The *commands* Table
The table *commands* in an enhanced relational-database has
the fields (with domains):
| | PRI name symbol
parameters parameter-list
procedure expression
documentation string
|
The parameters field is a foreign key (domain
parameter-list) of the *catalog-data* table and should
have the value of a table described by *parameter-columns*. This
parameter-list table describes the arguments suitable for passing
to the associated command. The intent of this table is to be of a form
such that different user-interfaces (for instance, pull-down menus or
plain-text queries) can operate from the same table. A
parameter-list table has the following fields:
| | PRI index ordinal
name symbol
arity parameter-arity
domain domain
defaulter expression
expander expression
documentation string
|
The arity field can take the values:
single
- Requires a single parameter of the specified domain.
optional
- A single parameter of the specified domain or zero parameters is
acceptable.
boolean
- A single boolean parameter or zero parameters (in which case
#f
is substituted) is acceptable.
nary
- Any number of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
nary1
- One or more of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
The domain field specifies the domain which a parameter or
parameters in the indexth field must satisfy.
The defaulter field is an expression whose value is either
#f or a procedure of one argument (the parameter-list) which
returns a list of the default value or values as appropriate.
Note that since the defaulter procedure is called every time a
default parameter is needed for this column, sticky defaults can
be implemented using shared state with the domain-integrity-rule.
6.1.4.5 Command Service
- Function: make-command-server rdb table-name
- Returns a procedure of 2 arguments, a (symbol) command and a call-back
procedure. When this returned procedure is called, it looks up
command in table table-name and calls the call-back
procedure with arguments:
- command
- The command
- command-value
- The result of evaluating the expression in the procedure field of
table-name and calling it with rdb.
- parameter-name
- A list of the official name of each parameter. Corresponds to the
name field of the command's parameter-table.
- positions
- A list of the positive integer index of each parameter. Corresponds to
the
index field of the command's parameter-table.
- arities
- A list of the arities of each parameter. Corresponds to the
arity field of the command's parameter-table. For a
description of arity see table above.
- types
- A list of the type name of each parameter. Correspnds to the
type-id field of the contents of the domain of the
command's parameter-table.
- defaulters
- A list of the defaulters for each parameter. Corresponds to
the
defaulters field of the command's parameter-table.
- domain-integrity-rules
- A list of procedures (one for each parameter) which tests whether a
value for a parameter is acceptable for that parameter. The procedure
should be called with each datum in the list for
nary arity
parameters.
- aliases
- A list of lists of
(alias parameter-name). There can be
more than one alias per parameter-name.
For information about parameters, See section 4.4.4 Parameter lists.
6.1.4.6 Command Example
Here is an example of setting up a command with arguments and parsing
those arguments from a getopt style argument list
(see section 4.4.1 Getopt).
| | (require 'database-commands)
(require 'databases)
(require 'getopt-parameters)
(require 'parameters)
(require 'getopt)
(require 'fluid-let)
(require 'printf)
(define my-rdb (add-command-tables (create-database #f 'alist-table)))
(define-tables my-rdb
'(foo-params
*parameter-columns*
*parameter-columns*
((1 single-string single string
(lambda (pl) '("str")) #f "single string")
(2 nary-symbols nary symbol
(lambda (pl) '()) #f "zero or more symbols")
(3 nary1-symbols nary1 symbol
(lambda (pl) '(symb)) #f "one or more symbols")
(4 optional-number optional ordinal
(lambda (pl) '()) #f "zero or one number")
(5 flag boolean boolean
(lambda (pl) '(#f)) #f "a boolean flag")))
'(foo-pnames
((name string))
((parameter-index ordinal))
(("s" 1)
("single-string" 1)
("n" 2)
("nary-symbols" 2)
("N" 3)
("nary1-symbols" 3)
("o" 4)
("optional-number" 4)
("f" 5)
("flag" 5)))
'(my-commands
((name symbol))
((parameters parameter-list)
(parameter-names parameter-name-translation)
(procedure expression)
(documentation string))
((foo
foo-params
foo-pnames
(lambda (rdb) (lambda args (print args)))
"test command arguments"))))
(define (dbutil:serve-command-line rdb command-table command argv)
(set! *argv* (if (vector? argv) (vector->list argv) argv))
((make-command-server rdb command-table)
command
(lambda (comname comval options positions
arities types defaulters dirs aliases)
(apply comval (getopt->arglist options positions
arities types defaulters dirs aliases)))))
(define (cmd . opts)
(fluid-let ((*optind* 1))
(printf "%-34s => "
(call-with-output-string
(lambda (pt) (write (cons 'cmd opts) pt))))
(set! opts (cons "cmd" opts))
(force-output)
(dbutil:serve-command-line
my-rdb 'my-commands 'foo (length opts) opts)))
(cmd) => ("str" () (symb) () #f)
(cmd "-f") => ("str" () (symb) () #t)
(cmd "--flag") => ("str" () (symb) () #t)
(cmd "-o177") => ("str" () (symb) (177) #f)
(cmd "-o" "177") => ("str" () (symb) (177) #f)
(cmd "--optional" "621") => ("str" () (symb) (621) #f)
(cmd "--optional=621") => ("str" () (symb) (621) #f)
(cmd "-s" "speciality") => ("speciality" () (symb) () #f)
(cmd "-sspeciality") => ("speciality" () (symb) () #f)
(cmd "--single" "serendipity") => ("serendipity" () (symb) () #f)
(cmd "--single=serendipity") => ("serendipity" () (symb) () #f)
(cmd "-n" "gravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "-ngravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "--nary" "chastity") => ("str" () (chastity) () #f)
(cmd "--nary=chastity" "") => ("str" () ( chastity) () #f)
(cmd "-N" "calamity") => ("str" () (calamity) () #f)
(cmd "-Ncalamity") => ("str" () (calamity) () #f)
(cmd "--nary1" "surety") => ("str" () (surety) () #f)
(cmd "--nary1=surety") => ("str" () (surety) () #f)
(cmd "-N" "levity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "-Nlevity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "--nary1=surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "-?")
-|
Usage: cmd [OPTION ARGUMENT ...] ...
-f, --flag
-o, --optional[=]<number>
-n, --nary[=]<symbols> ...
-N, --nary1[=]<symbols> ...
-s, --single[=]<string>
ERROR: getopt->parameter-list "unrecognized option" "-?"
|
6.1.5 Database Macros
(require 'within-database)
The object-oriented programming interface to SLIB relational databases
has failed to support clear, understandable, and modular code-writing
for database applications.
This seems to be a failure of the object-oriented paradigm where the
type of an object is not manifest (or even traceable) in source code.
within-database, along with the `databases' package,
reorganizes high-level database functions toward a more declarative
style. Using this package, one can tag database table and command
declarations for emacs:
| | etags -lscheme -r'/ *(define-\(command\5FT">[Contents] |
[Index] |
[ ? ] |
6.1.4.3 Define-tables Example
The following example shows a new database with the name of
`foo.db' being created with tables describing processor families
and processor/os/compiler combinations. The database is then
solidified; saved and changed to immutable.
| | (require 'databases)
(define my-rdb (create-database "foo.db" 'alist-table))
(define-tables my-rdb
'(processor-family
((family atom))
((also-ran processor-family))
((m68000 #f)
(m68030 m68000)
(i386 i8086)
(i8086 #f)
(powerpc #f)))
'(platform
((name symbol))
((processor processor-family)
(os symbol)
(compiler symbol))
((aix powerpc aix -)
(amiga-dice-c m68000 amiga dice-c)
(amiga-aztec m68000 amiga aztec)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(atari-st-gcc m68000 atari gcc)
(atari-st-turbo-c m68000 atari turbo-c)
(borland-c-3.1 i8086 ms-dos borland-c)
(djgpp i386 ms-dos gcc)
(linux i386 linux gcc)
(microsoft-c i8086 ms-dos microsoft-c)
(os/2-emx i386 os/2 gcc)
(turbo-c-2 i8086 ms-dos turbo-c)
(watcom-9.0 i386 ms-dos watcom))))
(solidify-database my-rdb)
|
6.1.4.4 The *commands* Table
The table *commands* in an enhanced relational-database has
the fields (with domains):
| | PRI name symbol
parameters parameter-list
procedure expression
documentation string
|
The parameters field is a foreign key (domain
parameter-list) of the *catalog-data* table and should
have the value of a table described by *parameter-columns*. This
parameter-list table describes the arguments suitable for passing
to the associated command. The intent of this table is to be of a form
such that different user-interfaces (for instance, pull-down menus or
plain-text queries) can operate from the same table. A
parameter-list table has the following fields:
| | PRI index ordinal
name symbol
arity parameter-arity
domain domain
defaulter expression
expander expression
documentation string
|
The arity field can take the values:
single
- Requires a single parameter of the specified domain.
optional
- A single parameter of the specified domain or zero parameters is
acceptable.
boolean
- A single boolean parameter or zero parameters (in which case
#f
is substituted) is acceptable.
nary
- Any number of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
nary1
- One or more of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
The domain field specifies the domain which a parameter or
parameters in the indexth field must satisfy.
The defaulter field is an expression whose value is either
#f or a procedure of one argument (the parameter-list) which
returns a list of the default value or values as appropriate.
Note that since the defaulter procedure is called every time a
default parameter is needed for this column, sticky defaults can
be implemented using shared state with the domain-integrity-rule.
6.1.4.5 Command Service
- Function: make-command-server rdb table-name
- Returns a procedure of 2 arguments, a (symbol) command and a call-back
procedure. When this returned procedure is called, it looks up
command in table table-name and calls the call-back
procedure with arguments:
- command
- The command
- command-value
- The result of evaluating the expression in the procedure field of
table-name and calling it with rdb.
- parameter-name
- A list of the official name of each parameter. Corresponds to the
name field of the command's parameter-table.
- positions
- A list of the positive integer index of each parameter. Corresponds to
the
index field of the command's parameter-table.
- arities
- A list of the arities of each parameter. Corresponds to the
arity field of the command's parameter-table. For a
description of arity see table above.
- types
- A list of the type name of each parameter. Correspnds to the
type-id field of the contents of the domain of the
command's parameter-table.
- defaulters
- A list of the defaulters for each parameter. Corresponds to
the
defaulters field of the command's parameter-table.
- domain-integrity-rules
- A list of procedures (one for each parameter) which tests whether a
value for a parameter is acceptable for that parameter. The procedure
should be called with each datum in the list for
nary arity
parameters.
- aliases
- A list of lists of
(alias parameter-name). There can be
more than one alias per parameter-name.
For information about parameters, See section 4.4.4 Parameter lists.
6.1.4.6 Command Example
Here is an example of setting up a command with arguments and parsing
those arguments from a getopt style argument list
(see section 4.4.1 Getopt).
| | (require 'database-commands)
(require 'databases)
(require 'getopt-parameters)
(require 'parameters)
(require 'getopt)
(require 'fluid-let)
(require 'printf)
(define my-rdb (add-command-tables (create-database #f 'alist-table)))
(define-tables my-rdb
'(foo-params
*parameter-columns*
*parameter-columns*
((1 single-string single string
(lambda (pl) '("str")) #f "single string")
(2 nary-symbols nary symbol
(lambda (pl) '()) #f "zero or more symbols")
(3 nary1-symbols nary1 symbol
(lambda (pl) '(symb)) #f "one or more symbols")
(4 optional-number optional ordinal
(lambda (pl) '()) #f "zero or one number")
(5 flag boolean boolean
(lambda (pl) '(#f)) #f "a boolean flag")))
'(foo-pnames
((name string))
((parameter-index ordinal))
(("s" 1)
("single-string" 1)
("n" 2)
("nary-symbols" 2)
("N" 3)
("nary1-symbols" 3)
("o" 4)
("optional-number" 4)
("f" 5)
("flag" 5)))
'(my-commands
((name symbol))
((parameters parameter-list)
(parameter-names parameter-name-translation)
(procedure expression)
(documentation string))
((foo
foo-params
foo-pnames
(lambda (rdb) (lambda args (print args)))
"test command arguments"))))
(define (dbutil:serve-command-line rdb command-table command argv)
(set! *argv* (if (vector? argv) (vector->list argv) argv))
((make-command-server rdb command-table)
command
(lambda (comname comval options positions
arities types defaulters dirs aliases)
(apply comval (getopt->arglist options positions
arities types defaulters dirs aliases)))))
(define (cmd . opts)
(fluid-let ((*optind* 1))
(printf "%-34s => "
(call-with-output-string
(lambda (pt) (write (cons 'cmd opts) pt))))
(set! opts (cons "cmd" opts))
(force-output)
(dbutil:serve-command-line
my-rdb 'my-commands 'foo (length opts) opts)))
(cmd) => ("str" () (symb) () #f)
(cmd "-f") => ("str" () (symb) () #t)
(cmd "--flag") => ("str" () (symb) () #t)
(cmd "-o177") => ("str" () (symb) (177) #f)
(cmd "-o" "177") => ("str" () (symb) (177) #f)
(cmd "--optional" "621") => ("str" () (symb) (621) #f)
(cmd "--optional=621") => ("str" () (symb) (621) #f)
(cmd "-s" "speciality") => ("speciality" () (symb) () #f)
(cmd "-sspeciality") => ("speciality" () (symb) () #f)
(cmd "--single" "serendipity") => ("serendipity" () (symb) () #f)
(cmd "--single=serendipity") => ("serendipity" () (symb) () #f)
(cmd "-n" "gravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "-ngravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "--nary" "chastity") => ("str" () (chastity) () #f)
(cmd "--nary=chastity" "") => ("str" () ( chastity) () #f)
(cmd "-N" "calamity") => ("str" () (calamity) () #f)
(cmd "-Ncalamity") => ("str" () (calamity) () #f)
(cmd "--nary1" "surety") => ("str" () (surety) () #f)
(cmd "--nary1=surety") => ("str" () (surety) () #f)
(cmd "-N" "levity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "-Nlevity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "--nary1=surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "-?")
-|
Usage: cmd [OPTION ARGUMENT ...] ...
-f, --flag
-o, --optional[=]<number>
-n, --nary[=]<symbols> ...
-N, --nary1[=]<symbols> ...
-s, --single[=]<string>
ERROR: getopt->parameter-list "unrecognized option" "-?"
|
6.1.5 Database Macros
(require 'within-database)
The object-oriented programming interface to SLIB relational databases
has failed to support clear, understandable, and modular code-writing
for database applications.
This seems to be a failure of the object-oriented paradigm where the
type of an object is not manifest (or even traceable) in source code.
within-database, along with the `databases' package,
reorganizes high-level database functions toward a more declarative
style. Using this package, one can tag database table and command
declarations for emacs:
| | etags -lscheme -r'/ *(define-\(command\5FT">[Contents] |
[Index] |
[ ? ] |
6.1.4.3 Define-tables Example
The following example shows a new database with the name of
`foo.db' being created with tables describing processor families
and processor/os/compiler combinations. The database is then
solidified; saved and changed to immutable.
| | (require 'databases)
(define my-rdb (create-database "foo.db" 'alist-table))
(define-tables my-rdb
'(processor-family
((family atom))
((also-ran processor-family))
((m68000 #f)
(m68030 m68000)
(i386 i8086)
(i8086 #f)
(powerpc #f)))
'(platform
((name symbol))
((processor processor-family)
(os symbol)
(compiler symbol))
((aix powerpc aix -)
(amiga-dice-c m68000 amiga dice-c)
(amiga-aztec m68000 amiga aztec)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(atari-st-gcc m68000 atari gcc)
(atari-st-turbo-c m68000 atari turbo-c)
(borland-c-3.1 i8086 ms-dos borland-c)
(djgpp i386 ms-dos gcc)
(linux i386 linux gcc)
(microsoft-c i8086 ms-dos microsoft-c)
(os/2-emx i386 os/2 gcc)
(turbo-c-2 i8086 ms-dos turbo-c)
(watcom-9.0 i386 ms-dos watcom))))
(solidify-database my-rdb)
|
6.1.4.4 The *commands* Table
The table *commands* in an enhanced relational-database has
the fields (with domains):
| | PRI name symbol
parameters parameter-list
procedure expression
documentation string
|
The parameters field is a foreign key (domain
parameter-list) of the *catalog-data* table and should
have the value of a table described by *parameter-columns*. This
parameter-list table describes the arguments suitable for passing
to the associated command. The intent of this table is to be of a form
such that different user-interfaces (for instance, pull-down menus or
plain-text queries) can operate from the same table. A
parameter-list table has the following fields:
| | PRI index ordinal
name symbol
arity parameter-arity
domain domain
defaulter expression
expander expression
documentation string
|
The arity field can take the values:
single
- Requires a single parameter of the specified domain.
optional
- A single parameter of the specified domain or zero parameters is
acceptable.
boolean
- A single boolean parameter or zero parameters (in which case
#f
is substituted) is acceptable.
nary
- Any number of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
nary1
- One or more of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
The domain field specifies the domain which a parameter or
parameters in the indexth field must satisfy.
The defaulter field is an expression whose value is either
#f or a procedure of one argument (the parameter-list) which
returns a list of the default value or values as appropriate.
Note that since the defaulter procedure is called every time a
default parameter is needed for this column, sticky defaults can
be implemented using shared state with the domain-integrity-rule.
6.1.4.5 Command Service
- Function: make-command-server rdb table-name
- Returns a procedure of 2 arguments, a (symbol) command and a call-back
procedure. When this returned procedure is called, it looks up
command in table table-name and calls the call-back
procedure with arguments:
- command
- The command
- command-value
- The result of evaluating the expression in the procedure field of
table-name and calling it with rdb.
- parameter-name
- A list of the official name of each parameter. Corresponds to the
name field of the command's parameter-table.
- positions
- A list of the positive integer index of each parameter. Corresponds to
the
index field of the command's parameter-table.
- arities
- A list of the arities of each parameter. Corresponds to the
arity field of the command's parameter-table. For a
description of arity see table above.
- types
- A list of the type name of each parameter. Correspnds to the
type-id field of the contents of the domain of the
command's parameter-table.
- defaulters
- A list of the defaulters for each parameter. Corresponds to
the
defaulters field of the command's parameter-table.
- domain-integrity-rules
- A list of procedures (one for each parameter) which tests whether a
value for a parameter is acceptable for that parameter. The procedure
should be called with each datum in the list for
nary arity
parameters.
- aliases
- A list of lists of
(alias parameter-name). There can be
more than one alias per parameter-name.
For information about parameters, See section 4.4.4 Parameter lists.
6.1.4.6 Command Example
Here is an example of setting up a command with arguments and parsing
those arguments from a getopt style argument list
(see section 4.4.1 Getopt).
| | (require 'database-commands)
(require 'databases)
(require 'getopt-parameters)
(require 'parameters)
(require 'getopt)
(require 'fluid-let)
(require 'printf)
(define my-rdb (add-command-tables (create-database #f 'alist-table)))
(define-tables my-rdb
'(foo-params
*parameter-columns*
*parameter-columns*
((1 single-string single string
(lambda (pl) '("str")) #f "single string")
(2 nary-symbols nary symbol
(lambda (pl) '()) #f "zero or more symbols")
(3 nary1-symbols nary1 symbol
(lambda (pl) '(symb)) #f "one or more symbols")
(4 optional-number optional ordinal
(lambda (pl) '()) #f "zero or one number")
(5 flag boolean boolean
(lambda (pl) '(#f)) #f "a boolean flag")))
'(foo-pnames
((name string))
((parameter-index ordinal))
(("s" 1)
("single-string" 1)
("n" 2)
("nary-symbols" 2)
("N" 3)
("nary1-symbols" 3)
("o" 4)
("optional-number" 4)
("f" 5)
("flag" 5)))
'(my-commands
((name symbol))
((parameters parameter-list)
(parameter-names parameter-name-translation)
(procedure expression)
(documentation string))
((foo
foo-params
foo-pnames
(lambda (rdb) (lambda args (print args)))
"test command arguments"))))
(define (dbutil:serve-command-line rdb command-table command argv)
(set! *argv* (if (vector? argv) (vector->list argv) argv))
((make-command-server rdb command-table)
command
(lambda (comname comval options positions
arities types defaulters dirs aliases)
(apply comval (getopt->arglist options positions
arities types defaulters dirs aliases)))))
(define (cmd . opts)
(fluid-let ((*optind* 1))
(printf "%-34s => "
(call-with-output-string
(lambda (pt) (write (cons 'cmd opts) pt))))
(set! opts (cons "cmd" opts))
(force-output)
(dbutil:serve-command-line
my-rdb 'my-commands 'foo (length opts) opts)))
(cmd) => ("str" () (symb) () #f)
(cmd "-f") => ("str" () (symb) () #t)
(cmd "--flag") => ("str" () (symb) () #t)
(cmd "-o177") => ("str" () (symb) (177) #f)
(cmd "-o" "177") => ("str" () (symb) (177) #f)
(cmd "--optional" "621") => ("str" () (symb) (621) #f)
(cmd "--optional=621") => ("str" () (symb) (621) #f)
(cmd "-s" "speciality") => ("speciality" () (symb) () #f)
(cmd "-sspeciality") => ("speciality" () (symb) () #f)
(cmd "--single" "serendipity") => ("serendipity" () (symb) () #f)
(cmd "--single=serendipity") => ("serendipity" () (symb) () #f)
(cmd "-n" "gravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "-ngravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "--nary" "chastity") => ("str" () (chastity) () #f)
(cmd "--nary=chastity" "") => ("str" () ( chastity) () #f)
(cmd "-N" "calamity") => ("str" () (calamity) () #f)
(cmd "-Ncalamity") => ("str" () (calamity) () #f)
(cmd "--nary1" "surety") => ("str" () (surety) () #f)
(cmd "--nary1=surety") => ("str" () (surety) () #f)
(cmd "-N" "levity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "-Nlevity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "--nary1=surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "-?")
-|
Usage: cmd [OPTION ARGUMENT ...] ...
-f, --flag
-o, --optional[=]<number>
-n, --nary[=]<symbols> ...
-N, --nary1[=]<symbols> ...
-s, --single[=]<string>
ERROR: getopt->parameter-list "unrecognized option" "-?"
|
6.1.5 Database Macros
(require 'within-database)
The object-oriented programming interface to SLIB relational databases
has failed to support clear, understandable, and modular code-writing
for database applications.
This seems to be a failure of the object-oriented paradigm where the
type of an object is not manifest (or even traceable) in source code.
within-database, along with the `databases' package,
reorganizes high-level database functions toward a more declarative
style. Using this package, one can tag database table and command
declarations for emacs:
| | etags -lscheme -r'/ *(define-\(command\5FT">[Contents] |
[Index] |
[ ? ] |
6.1.4.3 Define-tables Example
The following example shows a new database with the name of
`foo.db' being created with tables describing processor families
and processor/os/compiler combinations. The database is then
solidified; saved and changed to immutable.
| | (require 'databases)
(define my-rdb (create-database "foo.db" 'alist-table))
(define-tables my-rdb
'(processor-family
((family atom))
((also-ran processor-family))
((m68000 #f)
(m68030 m68000)
(i386 i8086)
(i8086 #f)
(powerpc #f)))
'(platform
((name symbol))
((processor processor-family)
(os symbol)
(compiler symbol))
((aix powerpc aix -)
(amiga-dice-c m68000 amiga dice-c)
(amiga-aztec m68000 amiga aztec)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(atari-st-gcc m68000 atari gcc)
(atari-st-turbo-c m68000 atari turbo-c)
(borland-c-3.1 i8086 ms-dos borland-c)
(djgpp i386 ms-dos gcc)
(linux i386 linux gcc)
(microsoft-c i8086 ms-dos microsoft-c)
(os/2-emx i386 os/2 gcc)
(turbo-c-2 i8086 ms-dos turbo-c)
(watcom-9.0 i386 ms-dos watcom))))
(solidify-database my-rdb)
|
6.1.4.4 The *commands* Table
The table *commands* in an enhanced relational-database has
the fields (with domains):
| | PRI name symbol
parameters parameter-list
procedure expression
documentation string
|
The parameters field is a foreign key (domain
parameter-list) of the *catalog-data* table and should
have the value of a table described by *parameter-columns*. This
parameter-list table describes the arguments suitable for passing
to the associated command. The intent of this table is to be of a form
such that different user-interfaces (for instance, pull-down menus or
plain-text queries) can operate from the same table. A
parameter-list table has the following fields:
| | PRI index ordinal
name symbol
arity parameter-arity
domain domain
defaulter expression
expander expression
documentation string
|
The arity field can take the values:
single
- Requires a single parameter of the specified domain.
optional
- A single parameter of the specified domain or zero parameters is
acceptable.
boolean
- A single boolean parameter or zero parameters (in which case
#f
is substituted) is acceptable.
nary
- Any number of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
nary1
- One or more of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
The domain field specifies the domain which a parameter or
parameters in the indexth field must satisfy.
The defaulter field is an expression whose value is either
#f or a procedure of one argument (the parameter-list) which
returns a list of the default value or values as appropriate.
Note that since the defaulter procedure is called every time a
default parameter is needed for this column, sticky defaults can
be implemented using shared state with the domain-integrity-rule.
6.1.4.5 Command Service
- Function: make-command-server rdb table-name
- Returns a procedure of 2 arguments, a (symbol) command and a call-back
procedure. When this returned procedure is called, it looks up
command in table table-name and calls the call-back
procedure with arguments:
- command
- The command
- command-value
- The result of evaluating the expression in the procedure field of
table-name and calling it with rdb.
- parameter-name
- A list of the official name of each parameter. Corresponds to the
name field of the command's parameter-table.
- positions
- A list of the positive integer index of each parameter. Corresponds to
the
index field of the command's parameter-table.
- arities
- A list of the arities of each parameter. Corresponds to the
arity field of the command's parameter-table. For a
description of arity see table above.
- types
- A list of the type name of each parameter. Correspnds to the
type-id field of the contents of the domain of the
command's parameter-table.
- defaulters
- A list of the defaulters for each parameter. Corresponds to
the
defaulters field of the command's parameter-table.
- domain-integrity-rules
- A list of procedures (one for each parameter) which tests whether a
value for a parameter is acceptable for that parameter. The procedure
should be called with each datum in the list for
nary arity
parameters.
- aliases
- A list of lists of
(alias parameter-name). There can be
more than one alias per parameter-name.
For information about parameters, See section 4.4.4 Parameter lists.
6.1.4.6 Command Example
Here is an example of setting up a command with arguments and parsing
those arguments from a getopt style argument list
(see section 4.4.1 Getopt).
| | (require 'database-commands)
(require 'databases)
(require 'getopt-parameters)
(require 'parameters)
(require 'getopt)
(require 'fluid-let)
(require 'printf)
(define my-rdb (add-command-tables (create-database #f 'alist-table)))
(define-tables my-rdb
'(foo-params
*parameter-columns*
*parameter-columns*
((1 single-string single string
(lambda (pl) '("str")) #f "single string")
(2 nary-symbols nary symbol
(lambda (pl) '()) #f "zero or more symbols")
(3 nary1-symbols nary1 symbol
(lambda (pl) '(symb)) #f "one or more symbols")
(4 optional-number optional ordinal
(lambda (pl) '()) #f "zero or one number")
(5 flag boolean boolean
(lambda (pl) '(#f)) #f "a boolean flag")))
'(foo-pnames
((name string))
((parameter-index ordinal))
(("s" 1)
("single-string" 1)
("n" 2)
("nary-symbols" 2)
("N" 3)
("nary1-symbols" 3)
("o" 4)
("optional-number" 4)
("f" 5)
("flag" 5)))
'(my-commands
((name symbol))
((parameters parameter-list)
(parameter-names parameter-name-translation)
(procedure expression)
(documentation string))
((foo
foo-params
foo-pnames
(lambda (rdb) (lambda args (print args)))
"test command arguments"))))
(define (dbutil:serve-command-line rdb command-table command argv)
(set! *argv* (if (vector? argv) (vector->list argv) argv))
((make-command-server rdb command-table)
command
(lambda (comname comval options positions
arities types defaulters dirs aliases)
(apply comval (getopt->arglist options positions
arities types defaulters dirs aliases)))))
(define (cmd . opts)
(fluid-let ((*optind* 1))
(printf "%-34s => "
(call-with-output-string
(lambda (pt) (write (cons 'cmd opts) pt))))
(set! opts (cons "cmd" opts))
(force-output)
(dbutil:serve-command-line
my-rdb 'my-commands 'foo (length opts) opts)))
(cmd) => ("str" () (symb) () #f)
(cmd "-f") => ("str" () (symb) () #t)
(cmd "--flag") => ("str" () (symb) () #t)
(cmd "-o177") => ("str" () (symb) (177) #f)
(cmd "-o" "177") => ("str" () (symb) (177) #f)
(cmd "--optional" "621") => ("str" () (symb) (621) #f)
(cmd "--optional=621") => ("str" () (symb) (621) #f)
(cmd "-s" "speciality") => ("speciality" () (symb) () #f)
(cmd "-sspeciality") => ("speciality" () (symb) () #f)
(cmd "--single" "serendipity") => ("serendipity" () (symb) () #f)
(cmd "--single=serendipity") => ("serendipity" () (symb) () #f)
(cmd "-n" "gravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "-ngravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "--nary" "chastity") => ("str" () (chastity) () #f)
(cmd "--nary=chastity" "") => ("str" () ( chastity) () #f)
(cmd "-N" "calamity") => ("str" () (calamity) () #f)
(cmd "-Ncalamity") => ("str" () (calamity) () #f)
(cmd "--nary1" "surety") => ("str" () (surety) () #f)
(cmd "--nary1=surety") => ("str" () (surety) () #f)
(cmd "-N" "levity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "-Nlevity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "--nary1=surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "-?")
-|
Usage: cmd [OPTION ARGUMENT ...] ...
-f, --flag
-o, --optional[=]<number>
-n, --nary[=]<symbols> ...
-N, --nary1[=]<symbols> ...
-s, --single[=]<string>
ERROR: getopt->parameter-list "unrecognized option" "-?"
|
6.1.5 Database Macros
(require 'within-database)
The object-oriented programming interface to SLIB relational databases
has failed to support clear, understandable, and modular code-writing
for database applications.
This seems to be a failure of the object-oriented paradigm where the
type of an object is not manifest (or even traceable) in source code.
within-database, along with the `databases' package,
reorganizes high-level database functions toward a more declarative
style. Using this package, one can tag database table and command
declarations for emacs:
| | etags -lscheme -r'/ *(define-\(command\5FT">[Contents] |
[Index] |
[ ? ] |
6.1.4.3 Define-tables Example
The following example shows a new database with the name of
`foo.db' being created with tables describing processor families
and processor/os/compiler combinations. The database is then
solidified; saved and changed to immutable.
| | (require 'databases)
(define my-rdb (create-database "foo.db" 'alist-table))
(define-tables my-rdb
'(processor-family
((family atom))
((also-ran processor-family))
((m68000 #f)
(m68030 m68000)
(i386 i8086)
(i8086 #f)
(powerpc #f)))
'(platform
((name symbol))
((processor processor-family)
(os symbol)
(compiler symbol))
((aix powerpc aix -)
(amiga-dice-c m68000 amiga dice-c)
(amiga-aztec m68000 amiga aztec)
(amiga-sas/c-5.10 m68000 amiga sas/c)
(atari-st-gcc m68000 atari gcc)
(atari-st-turbo-c m68000 atari turbo-c)
(borland-c-3.1 i8086 ms-dos borland-c)
(djgpp i386 ms-dos gcc)
(linux i386 linux gcc)
(microsoft-c i8086 ms-dos microsoft-c)
(os/2-emx i386 os/2 gcc)
(turbo-c-2 i8086 ms-dos turbo-c)
(watcom-9.0 i386 ms-dos watcom))))
(solidify-database my-rdb)
|
6.1.4.4 The *commands* Table
The table *commands* in an enhanced relational-database has
the fields (with domains):
| | PRI name symbol
parameters parameter-list
procedure expression
documentation string
|
The parameters field is a foreign key (domain
parameter-list) of the *catalog-data* table and should
have the value of a table described by *parameter-columns*. This
parameter-list table describes the arguments suitable for passing
to the associated command. The intent of this table is to be of a form
such that different user-interfaces (for instance, pull-down menus or
plain-text queries) can operate from the same table. A
parameter-list table has the following fields:
| | PRI index ordinal
name symbol
arity parameter-arity
domain domain
defaulter expression
expander expression
documentation string
|
The arity field can take the values:
single
- Requires a single parameter of the specified domain.
optional
- A single parameter of the specified domain or zero parameters is
acceptable.
boolean
- A single boolean parameter or zero parameters (in which case
#f
is substituted) is acceptable.
nary
- Any number of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
nary1
- One or more of parameters of the specified domain are acceptable. The
argument passed to the command function is always a list of the
parameters.
The domain field specifies the domain which a parameter or
parameters in the indexth field must satisfy.
The defaulter field is an expression whose value is either
#f or a procedure of one argument (the parameter-list) which
returns a list of the default value or values as appropriate.
Note that since the defaulter procedure is called every time a
default parameter is needed for this column, sticky defaults can
be implemented using shared state with the domain-integrity-rule.
6.1.4.5 Command Service
- Function: make-command-server rdb table-name
- Returns a procedure of 2 arguments, a (symbol) command and a call-back
procedure. When this returned procedure is called, it looks up
command in table table-name and calls the call-back
procedure with arguments:
- command
- The command
- command-value
- The result of evaluating the expression in the procedure field of
table-name and calling it with rdb.
- parameter-name
- A list of the official name of each parameter. Corresponds to the
name field of the command's parameter-table.
- positions
- A list of the positive integer index of each parameter. Corresponds to
the
index field of the command's parameter-table.
- arities
- A list of the arities of each parameter. Corresponds to the
arity field of the command's parameter-table. For a
description of arity see table above.
- types
- A list of the type name of each parameter. Correspnds to the
type-id field of the contents of the domain of the
command's parameter-table.
- defaulters
- A list of the defaulters for each parameter. Corresponds to
the
defaulters field of the command's parameter-table.
- domain-integrity-rules
- A list of procedures (one for each parameter) which tests whether a
value for a parameter is acceptable for that parameter. The procedure
should be called with each datum in the list for
nary arity
parameters.
- aliases
- A list of lists of
(alias parameter-name). There can be
more than one alias per parameter-name.
For information about parameters, See section 4.4.4 Parameter lists.
6.1.4.6 Command Example
Here is an example of setting up a command with arguments and parsing
those arguments from a getopt style argument list
(see section 4.4.1 Getopt).
| | (require 'database-commands)
(require 'databases)
(require 'getopt-parameters)
(require 'parameters)
(require 'getopt)
(require 'fluid-let)
(require 'printf)
(define my-rdb (add-command-tables (create-database #f 'alist-table)))
(define-tables my-rdb
'(foo-params
*parameter-columns*
*parameter-columns*
((1 single-string single string
(lambda (pl) '("str")) #f "single string")
(2 nary-symbols nary symbol
(lambda (pl) '()) #f "zero or more symbols")
(3 nary1-symbols nary1 symbol
(lambda (pl) '(symb)) #f "one or more symbols")
(4 optional-number optional ordinal
(lambda (pl) '()) #f "zero or one number")
(5 flag boolean boolean
(lambda (pl) '(#f)) #f "a boolean flag")))
'(foo-pnames
((name string))
((parameter-index ordinal))
(("s" 1)
("single-string" 1)
("n" 2)
("nary-symbols" 2)
("N" 3)
("nary1-symbols" 3)
("o" 4)
("optional-number" 4)
("f" 5)
("flag" 5)))
'(my-commands
((name symbol))
((parameters parameter-list)
(parameter-names parameter-name-translation)
(procedure expression)
(documentation string))
((foo
foo-params
foo-pnames
(lambda (rdb) (lambda args (print args)))
"test command arguments"))))
(define (dbutil:serve-command-line rdb command-table command argv)
(set! *argv* (if (vector? argv) (vector->list argv) argv))
((make-command-server rdb command-table)
command
(lambda (comname comval options positions
arities types defaulters dirs aliases)
(apply comval (getopt->arglist options positions
arities types defaulters dirs aliases)))))
(define (cmd . opts)
(fluid-let ((*optind* 1))
(printf "%-34s => "
(call-with-output-string
(lambda (pt) (write (cons 'cmd opts) pt))))
(set! opts (cons "cmd" opts))
(force-output)
(dbutil:serve-command-line
my-rdb 'my-commands 'foo (length opts) opts)))
(cmd) => ("str" () (symb) () #f)
(cmd "-f") => ("str" () (symb) () #t)
(cmd "--flag") => ("str" () (symb) () #t)
(cmd "-o177") => ("str" () (symb) (177) #f)
(cmd "-o" "177") => ("str" () (symb) (177) #f)
(cmd "--optional" "621") => ("str" () (symb) (621) #f)
(cmd "--optional=621") => ("str" () (symb) (621) #f)
(cmd "-s" "speciality") => ("speciality" () (symb) () #f)
(cmd "-sspeciality") => ("speciality" () (symb) () #f)
(cmd "--single" "serendipity") => ("serendipity" () (symb) () #f)
(cmd "--single=serendipity") => ("serendipity" () (symb) () #f)
(cmd "-n" "gravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "-ngravity" "piety") => ("str" () (piety gravity) () #f)
(cmd "--nary" "chastity") => ("str" () (chastity) () #f)
(cmd "--nary=chastity" "") => ("str" () ( chastity) () #f)
(cmd "-N" "calamity") => ("str" () (calamity) () #f)
(cmd "-Ncalamity") => ("str" () (calamity) () #f)
(cmd "--nary1" "surety") => ("str" () (surety) () #f)
(cmd "--nary1=surety") => ("str" () (surety) () #f)
(cmd "-N" "levity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "-Nlevity" "fealty") => ("str" () (fealty levity) () #f)
(cmd "--nary1" "surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "--nary1=surety" "brevity") => ("str" () (brevity surety) () #f)
(cmd "-?")
-|
Usage: cmd [OPTION ARGUMENT ...] ...
-f, --flag
-o, --optional[=]<number>
-n, --nary[=]<symbols> ...
-N, --nary1[=]<symbols> ...
-s, --single[=]<string>
ERROR: getopt->parameter-list "unrecognized option" "-?"
|
6.1.5 Database Macros
(require 'within-database)
The object-oriented programming interface to SLIB relational databases
has failed to support clear, understandable, and modular code-writing
for database applications.
This seems to be a failure of the object-oriented paradigm where the
type of an object is not manifest (or even traceable) in source code.
within-database, along with the `databases