This document describes ttn-pers-scheme 0.49: a collection of Guile Scheme code, documentation and maintenance methodology, released under the GNU GPL version 3.
You should have received a copy of the GNU General Public License along with this software; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Alphabetical list of all modules
Joy of implementation is a fine fix for a while. Getting twisted and experiencing KFJC and KSCM are the nice touch. Satisfying.
Personal mistakes (when recognized) are a sharp blade. Toolsmithing veers between kludge and laser. Code is data for tools, of course.
Personal expression is never original, they say. That's ok, too.
All this is to say: from form flows function, if given the right form. In this case, the library is the form I choose, and the experience arising from its maintenance is the function I seek. From this experience hopefully I can learn the ways of the masters.
To keep things simple all modules are in the ttn namespace with
names that look something like:
(ttn save-buffer) ;; ttn/save-buffer.scm
(ttn edit) ;; ttn/edit.scm
(ttn parse-rfc822) ;; ttn/parse-rfc822.scm
The corresponding filename, shown to the right of the module name, is
the one used in both the distribution and the installation. This naming
convention conforms to the guile module system's module name to filename
resolution algorithm, as long as the parent of the ttn directory
is included in the %load-path list. For normal installation
(i.e., make install), that parent directory would be guile's
non-version-specific site-lisp directory.
Each module is declared using a define-module form that: names
the module; names those modules upon which it depends; and in some
cases, names the exported variables. In all other cases,
exported variables are named in a separate export
form. Each module's source file has exactly one define-module
form, although sometimes there may be more than one export form.
The functional organization of the library is the subject of the rest of this guide. In addition to a linear listing of the modules and their related commentary (see In Theory), we present common functional groupings based on specific programming tasks, to give a better feel of what modules work well together (see In Practice).
The selected tasks vary in scope from the very small (still somewhat modular) to the very large (practically standalone application). Often there is additional critique of the library and/or the task drawn from the experiences of the author. May the gray hairs be put to good use!
Here is the list of all the (ttn foo) modules, sorted
alphabetically by the foo part. In theory, this is all the
documentation you need to make use of ttn-pers-scheme, but reference
material alone does not a good guide make, so feel free to skip to the
next chapter (see In Practice) and return here later.
This module provides procedures to formulate and parse tables of strings
that may have abbreviations. In gdb(1), for example, ‘i b’
means the same as “info breakpoints”. Although this is modeled after
gdb's admirable help system, the code is generic w/ the gdb-specific stuff
factored as gdb-style-WHATEVER functions.
These are low-level record-manipulation procedures:
(make-ab fullname) ⇒ abbrev-tree record
(ab? object) ⇒ bool
(ab-fullname ab) ⇒ string
(ab-children ab) ⇒ list of children abbrev-trees
(ab-children-set! ab list)
(ab-data ab) ⇒ data associated with abbrev-tree ab
(ab-data-set! ab data)
(make-aba alias ab) ⇒ abbrev-tree-alias record
(aba? object) ⇒ bool
(aba-alias aba) ⇒ alias
(aba-ab aba) ⇒ ab
These procedures display messages in GDB's style:
The rest of the procedures are the high-level interface.
See if NAME is visible as a child in TREE, as either an abbrev tree or an alias. If found, STURDY is called with the fullname of the child. If not found, SHAKY is called with NAME and TREE as args. If there is ambiguity, AMBIGUOUS is called with args NAME, TREE and a list of possible fullnames.
At this time, the optional arg SO-FAR is not used, but it will probably be tacked on as the optional arg for STURDY, SHAKY and AMBIGUOUS at some point (as a place for caller-defined data).
If module configuration variable `%careful%' is #t, children that are neither abbrev trees nor aliases throw a `bad-child' error. Otherwise, such children result in an immediate call to SHAKY as described above.
Add to TREE by splitting up FULLNAME-PATH into constituent words and creating children trees. FULLNAME-PATH is scrubbed before splitting. If FULLNAME-PATH already has been added, an error is signalled. [But what about `DATA' ?!?]
Translate IN-DATA, a nested list, into an abbrev tree and return it. For each list in IN-DATA, the car becomes a fullname and the CDR the children. Elements of the cdr can be lists, in which case `abtree-import' is called recursively. This procedure is experimental and may go away.
Recursively add alias children to TREE. Each alias's `ab' slot is either an abbrev tree when unambiguous, or a list of fullnames of possible abbrev trees, when ambiguous. Only TREE's children are consulted and referenced.
This module provides various convenient ways to use Guile 1.4.x's “editing-buffer” module. See Editing Buffer.
Perhaps a few of these procs will migrate into Guile proper at some point in the future.
In the following procs, if filename has #\$
or #\~ in it, it is expanded first with
expand-file-name-substituting-env-vars
(see expand-file-name).
Insert into gb the tree (of strings), walked depth-first. If gb is #f, create a new gap-buffer. Return the gap-buffer.
[NOTE: docs missing for find-file]
Write buffer into file filename. This makes the buffer visit that file. filename cannot name a directory.
Write buffer into file filename, but only if doing so would create a new file or a file with different contents than the pre-existing one. filename cannot name a directory.
Save buffer to disk. buffer should be visiting a file. An error is thrown if buffer is not visiting a file. An error is thrown if the file cannot be written. Return #t if all goes well.
This module exports the proc call-process-to-buffers, also
available as call-process->buffers.
Call program synchronously in separate process with optional args. Return a numeric exit status. Keyword args (all default values are
#f) are:
#:inb- Input buffer.
#:outb- Output buffer
#:errb- Error output buffer.
#:norm- Non-
#fmeans that program and args should be passed throughargs->normalized-list(see shellutils).
This module exports the proc call-process.
Call program synchronously in separate process with optional args. Unless give
#:outp 0, wait for program to terminate and return a numeric exit status.
#:inp (current-input-port)- Input port.
#fmeans /dev/null.#:outp (current-output-port)- Output port.
#fmeans discard output; 0 (zero) means discard, don't wait for program to terminate, and return#f.#:errp (current-error-port)- Error output port.
#fmeans discard output.#:norm- Non-
#fmeans program and args should be passed throughargs->normalized-list(see shellutils).
This file exports two macros, cron and cron!.
Examine cadr of cmdline-list and invoke one of the handlers.
cmdline-list is a two-element list, of the form:
(PROG-NAME COMMAND)handlers is zero or more forms:
((KEY) (BODY1) (BODY2) ...)body1, body2 and so on are optional. key is a symbol or string. The body forms for a key are executed if command matches key. If cmdline-list does not have exactly two elements, a usage message is displayed and a "bad usage" error is thrown. If command matches no key,
cronsilently returns #t, otherwise, the return value is the value of the last body form executed.For example:
#!/usr/local/bin/guile -s !# (use-modules ((ttn cron) #:select (cron))) (exit (cron (command-line) ((hourly) (display "yawn, stretch") (newline)) ((daily) (display "zonk") (newline))))
Call
cronwith(command-line)and handlers. Exit with the return value.
We only deal with the branch represented by the working directory. This could be considered a bug.
Run "cvs log" on file; return an alist describing the head revision. Keys include:
rcs-file,working-file,head,locker,date,authorandstate. All values are strings.
Display alist value r returned by Scheme function
cvslog. Optional args fields are symbols specifying which fields to display and in what order. If omitted, display, in this order:rcs-file,working-file,head,date,author,state.
For directory dir, summarize state info for files in CVS/Entries. Files are grouped by state and sorted w/ most-recently changed first.
This module exports eight procs and two macros:
(mkdir-p dir)
(directory-files dir)
(filtered-files filter dir)
(filtered-files-in-vicinity dir filter)
(not-dot-not-dotdot file)
(extract-stem-proc ext)
(filename-sans-end-sep-proc sep-char)
(file-newer-than-file? file1 file2)
(save-cwd body) <-- macro
(with-cwd body) <-- macro
Apply filter to each file name in dir; return list of non-#f values. filter is a procedure applied to each file name – it should return #f if that file name is to be omitted from the overall list, or a value to be collected. The file name list is not sorted.
In dir, apply filter to each file name; return list of non-#f values. filter is a procedure applied to to each filename – it should return #f if that file name is to be omitted from the overall list. Otherwise, its return value can be a string, or #t which is synonymous with the filename with the dir prefixed. The file name list is not sorted. options are zero or more keywords that modify the behavior:
#:filter-prefixed- Pass to filter each file with dir already prefixed.
#:collect-nodir- For the case when filter returns
#t, collect the filename without the dir prefix.NB: The argument order is opposite that of
filtered-files.
Return a procedure that filters based on ext, a string. The procedure takes a filename and if it ends in .EXT (note dot), returns the portion of the filename before the dot, otherwise #f. Optional arg transform controls precisely how the returned filename is to be processed. If transform is omitted or #f, the extension is discarded. If it is #t, the extension is left alone. If it is a string, the stem is concatenated with it. If it is a procedure, that procedure is applied to the stem, and it's return value, which need not be a string, is collected.
Take sep-char and return a procedure that, given filename, returns a copy of it w/o the ending sep-char.
Return #t if file file1 is newer than file file2. If file1 does not exist, the answer is #f; otherwise, if file2 does not exist, the answer is #t.
In the following macros, if body is null, the return value is unspecified, otherwise it is the value of the last expression in body.
Email who w/ subject the log of failed shell-command. who and subject are strings or symbols. shell-command is one or more strings or symbols, to be passed through
system*. If shell-command is successful, no email is sent. Log includes both stdout and stderr. Use "mail" for mailer. Return#tif shell-command is successful, otherwise#f.
Change the system mail program to program. Normally it is ‘mail’. program should behave similarly: it must support the command-line sequence
-ssubject recipient, and take the mail message body from stdin.
This module exports four procs:
(expand-file-name name [default-directory])
(reset-tilde-cache! [size])
(substitute-env-vars string)
(expand-file-name-substituting-env-vars name [default-directory])
Both expand-file-name and substitute-env-vars are
directly inspired by Emacs.
Convert filename name to absolute, and canonicalize it. Second arg default-directory is directory to start with if name is relative (does not start with slash); if default-directory is #f or missing,
(getcwd)is used. File name components that are . are removed, and so are file name components followed by .., along with the .. itself; note that these simplifications are done without checking the resulting file names in the file system. An initial ~/ expands to your home directory. An initial ~USER/ expands to USER's home directory.
Reset the cache
expand-file-nameuses for the results of expanding~and~USER. Optional arg size specifies the hash table bucket count to use (default is 7).
Substitute environment variables referred to in string.
$FOOwhere FOO is an environment variable name means to substitute the value of that variable. The variable name should be terminated with a character not a letter, digit or underscore; otherwise, enclose the entire variable name in braces. For instance, inab$cd-x,$cdis treated as an environment variable.Use
$$to insert a single dollar sign.
Substitute env vars in string then expand it as a filename. See
substitute-env-varsandexpand-file-name.
The procedures in this module use the (ice-9 ftw)
module from Guile. See Filesystem Tree Walk.
Return the filesystem tree rooted at filename as a nested list of node values as obtained by calling node-proc, a procedure that takes five arguments, on each directory and subdirecory. The arguments to node-proc are explained fully in the
nftwdocumentation.(node-proc name statinfo flag base level) => node valueIf filename is names a file (not a directory), simply return the node value resulting from one call to node-proc. Otherwise, for each list in the return value, the car is the node value of the directory and the cdr the node values of the children.
The remaining control-flags are passed through to
nftw. You need not specify the flagdepth; it is automatically included.
If name begins with "/", return it. Otherwise, return a new string composed by taking name in vicinity of dir.
Return name, a string, stripping the terminating "/" character. If there is no "/", just return name.
Return a list of filename components parsed from string. Components are delimited by "/", which is discarded. Null string components are also discarded.
Return a string composed by prefixing each element of ls with "/".
Walk tree depth-first, displaying elements if they are strings. Signal "bad type" error for non-string, non-list elements. Optional second arg out is a procedure to use instead of
display.
Flatten to port (using
flatten) the tree, a nested list of strings. If port is#freturn a string, instead.
This module exports eight procedures:
read-line-no-echo siginfo:signer
clearsign-message-interactively siginfo:time
verify-signed-message siginfo:method
siginfo:sig
siginfo:body
Quietly read a line from current input port, first displaying prompt. Typed characters are not echoed to the screen. Throw
bad-environmenterror if running under Emacs.
Invoke
gpg --clearsignon msg (a string or port), and return the output. Throwbad-environmenterror if running under Emacs.
Invoke
gpg --verifyon msg (a string or port), and return a siginfo value. The elements of this value (all strings) can be extracted using one of the "siginfo:foo" procedures. If the gpg call fails, throw asignature-verification-errorwith argument the gpg verifier. This verifier is created usingmake-buffered-caller, q.v.
The procs grep and grep-matches work on lists of strings.
Unfortunately, their names are slightly confusing to those used to the
shell-command command grep(1). On the other hand (to add to this
confusion), grep-l works on a list of files (or file ports).
Return list of matches to regexp re from strings list. Optional flags are keywords (or symbols with the same name) which modify regexp matching:
#:invert- the result list is those strings that do NOT match
#:fold-case- consider upper and lower case to be identical
#:count- return length of result list instead of list
#:literal- re specifies a literal string to match
Return list order is the same as in the input list.
Return non-#f match results of
regexp-execof regexp re on strings list. Optional flags are keywords (or symbols with the same name) which modify regexp matching:
#:invert- the result list is composed entirely of #t values, with length equal to the number of strings that do NOT match (useful with
countflag below)#:fold-case- consider upper and lower case to be identical
#:count- return length of result list instead of list
#:literal- re specifies a literal string to match
Return list order is the same as in the input list.
Search for regular expression re in files. Return a list of those that match. re specifies a regular expression that matches on one line (multi-line results not currently supported). files is a list, each element of which can either be a filename or a seekable port. In the returned list, if the element is a port, its read offset is left at the beginning of the line of the first match.
This module exports the following procs:
(html . x)
(href url text [name]) ; url can be #f
(name text)
(head . x)
(title . x)
(link . x)
(body . x)
(h1 . x) (h2 . x) ... (h6 . x)
(br)
(hr)
(b . x)
(p . x) ; no body => <P>; body => <P> x </P>
(tt . x)
(pre . x)
(tt-pre . x)
(div . x)
(span . x)
(textarea rows cols name x)
(input type name value)
(form method action . x)
(refresh sec url)
(li . x)
(ul . x) ; `li' for each element of x
(ull x) ; variant of `ul' that takes a single list
(dl . x) ; for each elem of x, car => <DT>, cdr => <DD>
(dll x) ; variant of `dl' that takes a single list
(table . x)
(tr . x)
(td . x)
(copyright . prefix)
(copyright-since year)
(center . x)
(em . x)
(strong . x)
(dfn . x)
(code . x)
(samp . x)
(kbd . x)
(var . x)
(cite . x)
(abbr title . x)
(blockquote . x)
(q . x)
(smhdwy-since diff) ; in seconds, may be negative
All procs return lists (possibly nested), suitable for walking with a “flattening” procedure, something like:
(define (output-html-data x)
(cond ((string? x) (actual-output x))
((list? x) (for-each output-html-data x))
(else (error "unknown type:" x))))
The proc actual-output might send x to a port, for example.
See flatten.
Return the tree:
("<?xml version=\"1.0\" encoding=\"" encoding "\"?>" :LF)
Return the tree:
("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 " type-as-capitalized-string "//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-" type-as-string ".dtd\">" :LF)type is one of
#:strict,#:transitional,#:frameset; or a symbol with the same name.
Following is the list of other exported procedures. Each expands (see markup) its arglist into a tree where the element name is the same as the procedure name, but without the tilde (‘~’).
~html
~head ~title ~base ~meta ~link ~style
~script ~noscript
~body
~div ~p ~h1 ~h2 ~h3 ~h4 ~h5 ~h6
~ul ~ol ~li ~dl ~dt ~dd
~address
~hr
~pre ~blockquote
~ins ~del
~a
~span
~bdo
~br
~em ~strong
~dfn ~code ~samp ~kbd ~var ~cite ~abbr ~acronym
~q ~sub ~sup
~tt ~i ~b ~big ~small
~object ~param
~img ~map ~area
~form ~label ~input ~select ~optgroup ~option
~textarea ~fieldset ~legend ~button
~table ~caption ~thead ~tfoot ~tbody ~colgroup ~col
~tr ~th ~td
Concatentate source, a shell command that writes image data to stdout, with a series of shell commands from the Netpbm package. Optional arg invert? non-
#fmeans to include a call to pnminvert in the pipeline.Execute the pipeline in a subshell and return its output as a string.
Profile string for small non-whitespace intervals to be “marked up” as the text of an HTML
Aelement, with reference taken randomly from list-of-links (list of strings). As as special case, if the car of list-of-links is the symbolfile-lines, then the cadr is taken to be a filename which must contain one link (URL) per line.Return an html-data tree (nested list of strings). Optional third arg link-tree specifies a procedure to use instead of href (see html-data) to construct hyperlink references. It should take arguments url and text (both strings), and return a tree (nested list of strings).
Return an html-data tree made from an ASCII-representation of a globe image. When optional arg links is specified, the single string is profiled for small non-whitespace intervals to be “marked up” as the text of an HTML
Aelement, with the reference taken randomly from links.links may be the name of a file containing one URL per line. links may also be a list or vector of URLs.
Keyword parameters, default values, and their meanings:
#:size 150 Size in pixels (NxN) of the originating image.
#:gen #:ppmforge Preferred backend program used to generate the image, one of #:xearth,#:xplanetor#:ppmforge. The value can also be a symbol.
#:link-tree href Another procedure to use instead of href(see html-data). It should take arguments url and text (both strings), and return a tree (nested list of strings).
Return a thunk that listens with ear, a list in of the form
(FAMILY ADDRESS [ARGS]). As a special case, if ear is an integer, it is taken to specify a TCP/IP port p and converted internally to familyPF_INET, addressINADDR_ANY, and args p. options are alternating keyword ands values:
#:setup-sock- A procedure taking one arg, the listener socket, called right before doing
bind. This is a good place forsetsockoptcalls.#:nqueue- Number of queued requests, default 10.
#:pre#:handle#:post- Procedures that take two args, port and conn, the output of
accept. These three procedures are thunkified to form the branches of adynamic-wind, the child. After calling the post procedure, port is closed. If pre, handle, and post are unspecified, the default action is to do nothing.#:concurrency- The value may be
#:coop-thread, in which case each child is run in its own thread. If it is #f or unspecified, child dispatch is serial.#:catch-tag- A symbol that will catch any calls to
throwthat uses it. Normally, the server loop uses#tto catch all throws. This tag (and indeed any args in the throw) are ignored.
Return a procedure capable of calling program w/ optional args. When called, the program stdout and stderr are captured to buffers. The key
#:inb(default #f) specifies an input buffer to use for the call.The returned procedure takes one of the following commands (either a keyword or similarly-named symbol):
#:redefine NEW-DEF- Redefine the called program and its args. new-def is one or more strings.
#:execute- Call program, clearing buffers first. Return raw exit status of the program. See
status:exit-valfor more info.#:execute/no-init- Same as
#:execute, but do not clear buffers first.#:outbuf- Return output buffer object.
#:outbuf-string- Return output buffer as a string.
#:outbuf-lines- Return output buffer as a list of strings.
#:errbuf- Return error output buffer.
#:errbuf-string- Return error output buffer as a string.
#:errbuf-lines- Return error output buffer as a list of strings.
#:exit-val- Return exit status from the last execution. Signal an error if
#:execute(or#:execute/no-init) command has not yet been issued since closure creation or most recent#:redefine.The
#:outbuf-linesand#:errbuf-linescommands use newline to separate.
In addition to the procs described below, the following named strings are available (NB: the colon is part of the variable name):
Scan list of the form:
([attr-name attr-value ...] [body...])Each attr-name is either a keyword or a symbol. Each attr-value is either a string, a (possibly) nested list of strings, a symbol or a number. When there are no more attribute names, the rest of the list (which may be null) is taken as the body.
Return a pair whose car is the attributes, formatted (in a tree) as
NAME="VALUE"; and whose cdr is the body.
Return a proc p that expands into a tree based on elem. Precisely, p partitions its arglist with
peelinto attributes and body and returns the tree:("<" elem attributes [" /"] ">" [neck] [body ["</" elem ">"]] [tail])Other args are keywords. Here is a list (with default value):
#:xbsc #f- Non-
#fmeans “XMLish (blech) start close”, i.e., the start tag should be rendered as ‘<ELEM />’ instead of of ‘<ELEM>’ (with space and slash before closing angle bracket). Typically, this is specified in conjunction with#:end-tag :NULL.#:neck :NULL- This is inserted between the initial tag and is not subjected to
prep-body. Typically:LFwhen specified.#:prep-body #f- This can be a procedure that is passed body and returns a transformed tree. It can also be a a pair whose car is the symbol
mapand whose cdr is a procedure taking one arg. The effective body is computed by mapping this procedure over all the top-level elements of body.#:no-end-tag-if-null-body? #f- Non-
#fspecifies that if body is the empty list, the ‘</ELEM>’ end tag should be omitted entirely.#:end-tag #f- Specifies an alternative end tag.
#:tail :NULL- A value to be appended at the very end of the tree. Typically
:LFwhen specified.
Return a tree made from expanding CSS rule, a list of the form:
(target [property value...])In this form, target can be a string or a flat list of strings; property can be a string, symbol, or keyword, and value can be a string or a nested list of strings.
Mixp is a Guile interface written by Thierry Bézecourt, to expat, an XML Parser written by James Clark.
Return a mixp parser with initial user data init and handlers. handlers is a 14-element vector, which is used by the procs
expat:set-FOO-handler, whereFOOand the handler elements are as follows:element 0 1 character-data 2 processing-instruction 3 comment 4 cdata-section 5 6 default 7 unparsed-entity-decl 8 namespace-decl 9 10 not-standalone 11 external-entity-ref 12 unknown-encoding 13 #fNote that for
expat:set-unknown-encoding-handler, the second arg is hardcoded to #f.
Call
mixp:parseon parser and afterwards, return its user data. Optional arg port specifies an input port, otherwise the current input port is used.
This module exports the proc remove-keys.
Remove keywords and their values from list
ls. Return a new list. For example:(remove-keys '(ok1 #:a 42 #:b 99 ok2 #:c #:d ok3)) ⇒ (ok1 ok2 ok3)
Parse mailmsg, return a query proc. mailmsg may be a list of lines (w/o terminating newline), a gap-buffer, or something that
make-gap-buffertakes. The query proc takes a symbol comp, and returns the associated message component. Supported values for comp are:
from- first whitespace-delimited token after "^From " on first line
headers- the headers as an alist (keys are strings), order maintained
body- rest of the mail message
Any other query results in a "bad component" error.
This module exports these procedures:
(get-meta-file) => filename
(set-meta-file! FILENAME)
(personal-pgtable-all) => alist
(personal-pgtable-defs DB TABLE) => defs
(personal-pgtable-manager DB TABLE) => pgtable-manager
(personal-pgtable-worker DB TABLE) => pgtable-worker
The meta file contains two forms (sexps), the first a list of
elements each of the form: (keyword expansion),
and the second a single alist of the form:
((DB-1 (TABLE DEF ...) (TABLE DEF ...) ...)
(DB-2 (TABLE DEF ...) (TABLE DEF ...) ...) ...)
Each db and table are strings, while def... are column definitions. See Column Definitions.
Wherever a keyword from the first form appears in defs, its expansion
(a string) is used instead. Env var TTN_PGTABLE_DEFS names this
file, otherwise it is taken to be ~/.pgtable-defs by default. This
is done at module-load time. To change the meta file after loading, use
procedure set-meta-file!.
We only deal with the main branch. This could be considered a bug.
Run rlog(1) on file; return an alist describing the head revision. Keys include:
rcs-file,working-file,head,locker,date,authorandstate. All values are strings.
Display alist value r returned by Scheme function
rlog. Optional args fields are symbols specifying which fields to display and in what order. If omitted, display, in this order:rcs-file,working-file,head,date,author,state.
For directory dir, summarize state info for files in subdir RCS. Files are grouped by state and sorted w/ most-recently changed first.
A text-db-table is a file that begins with zero or more introductory lines (ignored), followed by a Scheme form starting at the beginning of a line:
(text-db-table-config
(meta . META) ; optional; META is opaque, default #f
(delim . "\f")
(fields (NAME1 TYPE1)
(NAME2 TYPE2)
...))
name is a symbol or keyword; type is one of the symbols:
sexpread.
sexp-lineread, then discard trailing whitespace.
lineread-line, discarding eol chars (CR, LF).
rest-linesrest-lines-trimrest-lines but result is string-trim-bothed.
Following the Scheme form is text terminated by the delimiter (a form feed in the above example), which is also ignored. Following this are the records of the database, separated by the delimiter, until the end of the file. The delimiter should NOT be at the end of the file.
This module provides the procedure read-text-db-table and the
object property (procedure w/ setter) text-db-table-meta.
Read a text-db-table from filename/port, return a list of records. Each record is an alist whose keys are the field names, in order. filename/port can be a filename (string), or an input port. In the latter case, it is left open when done.
As a side effect, the property
text-db-table-meta(procedure with setter) for the returned list is set to the table's metadata, if any.flags is a list of zero or more keywords that change the default behavior. These are the recognized flags:
#:list- Return each record as a list instead of as an alist.
#:closure- Return each record as a closure (procedure) that accepts one arg sel. If
#:listis specified, sel is a 0-based integer to index into the record's data. Otherwise, sel names a field in the record's data.
The rlog(1) output format has stabilized for over ten years now. It is used by RCS and CVS (slightly modified). This module provides two procs that each return a scanner proc capable of parsing a string in rlog output format:
(rlog-scan!-proc boxed-keys) => na-scanner
(rlog-scan-proc keys) => scanner
Both na-scanner and scanner take a string and scan for regular expressions, specified by zero or more of the following keys:
rcs-file
working-file
head
locker
date
author
state
The difference is in who does the memory allocation. On one hand,
boxed-keys is a list of sublists, the car of which is a key and
the cdr of which is set by na-scanner (na stands for
“non-allocating”). The return value of na-scanner is
unspecified. On the other hand, keys is simply a list of keys,
and scanner constructs a fresh alist to return to the caller.
A secondary difference is that the alist modified by na-scanner
is never shortened in case a particular regular expression does not
match – na-scanner places a #f in the cdr – while
scanner simply omits such elements from its return value.
As a special case, both keys and boxed-keys may be
#f, which is taken to be the same as specifying all the keys.
Additionally, you can use group-logs-by to bin the logs
(typically, either by state or by author).
Group logs, a list of alists, by key in each alist. Return a list with elements of the form
(KEY L1 L2...), where key is a symbol and l1... are elements of logs.Optional args keep specify keys of those elements in l1... to collect. If unspecified, return the entire alist.
This module exports two procs:
(read-vcg-file filename) => vcg-tree
(write-vcg-file vcg-tree filename)
Presently, operation relies on the external program sed(1). This means to say that there is a hackish text transform going on in the background instead of “proper” parsing/unparsing, resulting in strange behavior for certain valid inputs.
Return the internal graph representation object created from reading VCG file filename.
Write vcg, an internal graph representation, to file filename. If filename is #t, write to stdout.
Prepend to var
documentation-filesfilename of ttn docs. The filename is computed using%search-load-path. Do not modify var if ttn/ttn-pers-scheme.gdf1 is not found.
Set up interactive session environment according to ttn preferences. Specifically, set debug depth, turn on backtrace (on error :-), make the repl verbose, remove "." from
%load-path, arrange for package docs to be findable, and extend the read-hash table so that#^Ffoo(where^Fis#\ackor "\C-f" in Emacs nomenclature) reads as a filename (string) formed as a result of passing it throughexpand-file-name-substituting-env-vars.
This file exports these procs:
(make-scat LEN . DEFAULT-VALUE)
(scat-len SCAT)
(scat-default SCAT)
(scat-concat SCAT1 SCAT2)
(make-sl FIELD-DEFS)
(sl-copy SL)
(sl-edit! SL FIELD-EDITS)
(sl-from-template SL FIELD-EDITS)
(sl->bytes SL . BYTE-SIZE)
len and default-value are numbers. field-defs have one of two forms:
(NAME LENGTH VALUE)
(NAME HEX-STRING)
In the latter form, length and value are computed from hex-string.
field-edits have the form: (name value).
Return a new scat of LEN bits, w/ DEFAULT-VALUE, a number. If DEFAULT-VALUE is omitted, it is taken as 0. If DEFAULT-VALUE takes more bits than LEN, it is silently truncated.
Return new scat made from SCAT1 and SCAT2. In the result, SCAT1 is shifted left and SCAT2 maintains low-order bits.
Return a new SL structure made from FIELD-DEFS, a list of elements of the form (NAME LENGTH VALUE) or (NAME HEX-STRING). In the latter case, LENGTH and VALUE are computed from HEX-STRING. NAME may be #f, in which means no name. FIELD-DEFS order is maintained in the returned SL.
Destructively modify SL using FIELD-EDITS, a list. Each element of FIELD-EDITS has the form: (NAME VALUE).
Copy SL and apply FIELD-EDITS to the new structure. Return the new. This is a non-destructive variant of `sl-edit!'.
Return list of byte values for SL, in network-byte order. Optional arg BYTE-SIZE indicates how many bits are to be included in a byte, default if omitted is 8. Signal error if SL aggregate bit length is not an integral multiple of the byte size.
This module provides the procs:
(read-sgf-file filename) => collection
(write-sgf-file collection filename)
(children tree) => list or #f
(nodes tree) => list
(get node prop) => list of values
(get-one node prop) => value
(analyze tree [prefix])
collection is a list of one or more game trees. A game tree is
a list w/ car #t and cdr a list of one or more nodes followed
by zero or more children game trees. A node is a list of pairs, the
car of which is a keyword representing an SGF property and the cdr
the associated value, typically a string, number, two-element vector,
or a pair of one of these simpler types. A pair for the property
value indicates a composed type.
The vector elements are integers specifying column and row zero-based
coordinates on a board. For example, #(4 2) represents
position E3. For board size n, the vector
#(n n) means “PASS”.
Additionally, this module also provides two data tables:
*properties*
*format-changes*
The data tables were originally from GNU Go 3.3.15 sources, massaged into Scheme and surrounded by reverse-engineered algorithms one summer night, sans net.cnxn.
List of property specifications. This is called the FF[4] property index by the GNU Go sources. Each entry has the form:
(NICK DESC CONTEXT VALUE-SPECS).
nick- One- or two-character symbol. (Note, however, that in the tree returned by
read-sgf-file, nick is represented by a keyword.) Some examples:B,W,FF.desc- A short string describing the property. Examples:
"Black","White","Fileformat".context- A symbol indicating where this property is likely to be found. [Perhaps the actual specification is more restrictive; the current parser ignores this information completely.] Examples:
move,setup,root,-.value-specs- One or more expressions that comprise a mini-language for describing the possible values for this property.
none- Symbol. The property has no value.
doublerealnumber- Symbols. The value is a number. The first two indicate that the number may be a real number (with component to the right of the radix point).
#(number (LOW . HIGH) ...)- Vector with symbol
numberat index zero, followed by one or more pairs of integers. The value is a number but constrained to the inclusive range(s) described by low and high. For example,#(number (3 . 5) (8 . 10))means that the value must be 3, 4, 5, 8, 9, or 10.textsimpletext- Symbols. The value is a string. Actually, these probably should be differentiated somehow; when we regain internet connection access to the actual spec will clarify things a bit...
color- Symbol. The value is either
blackorwhite.pointmove- Symbols. The value is a two-character codification of a vertice on the board, starting from
aa. Apparently the valuettmeans "PASS".(TYPE-1 . TYPE-2)- Pair of symbols. The value is a composed type, rendered as the first followed by a colon character (
:) followed by the second.(or number (number . number))- List of three elements, exactly as shown. The value can be a single number or a composed type of two numbers.
(or none TYPE)- List of three elements, the first two symbols
orandnone. The property may take no value, or one of type type (which may be simple or composed).list TYPEelist TYPE- Sequence (not a list) of symbol followed by further specification. The property can have multiple values, one after another. The difference between
listandelistis not clear at the moment.
Vector of elements specifying how the SGF format has changed. Each element is one of these values:
nil- no such format version (ERROR!)
- integer (same as index)
- no info for this format version
- pair
- car and cdr are, respectively, lists of changed and added property nicks
The expression
(1- (vector-length *format-changes*))evaluates to a number specifying the currently supported file format version.
Return the collection of game trees parsed from reading filename.
Return a list of children game trees for tree, or #f if tree has no children.
In node, return a list of all values for property prop. The list may be empty or it may contain several values.
In node, return the value associated with the first occurance of prop.
Display to the current output port a simple analysis of tree. (This is more for debugging support than anything serious.) Optional arg prefix specifies the number of spaces to insert at the beginning of the line, for recursive analysis of children trees.
This module is inspired by an Emacs Lisp function by the same name. It exports the following procedures:
(shell-command-to-string cmd) => string
(shell-command-to-list cmd [handle-delim]) => list of strings
(file-lines filename [handle-delim]) => list of strings
cmd is a string. handle-delim is an optional symbol that
controls how to handle the newline at the end of each line, one of:
trim, concat, peek, split. This is
passed to procedure read-line in module (ice-9 rdelim).
If unspecified, the default is trim.
If filename has #\~ or #\$ in it, it goes through
expand-file-name-substituting-env-vars (see expand-file-name).
For more info on optional arg handle-delim, See Line/Delimited.
Also exported are two aliases to the above procs whose names are formed by replacing the ‘-to-’ with ‘->’. Supposedly more schemey that way.
This module exports the following procs:
(system* . args)
(sysfmt . args)
(args->normalized-list . args)
Apply
simple-formatto args and pass tosystem. The car of args is the format string.
For args, trim surrounding whitespace, join together separated by space, then split apart on space boundaries. For example:
(args->normalized-list "rsync a b c" " d " "e" "f g h") ⇒ ("rsync" "a" "b" "c" "d" "e" "f" "g" "h")
This is an attempt at re-injecting slack due to Guile moving from string/symbol inter- to in-operability. Feh.
Return a procedure that concatenates its args to produce an object of type return-type, one of
stringor#:stringorsymbolor#:symbol.
This module exports the procs:
(eval-html-data-file file)
(spew-html! html-data outfile . log)
(update-page! template-file outfile . log)
(update-all-pages! source-page pages)
(update-all-html-data-pages! pages)
This chapter delves into one of the primary motivations for development of ttn-pers-scheme: dabbling in various personal projects.
It might seem strange to consider Learning Scheme Programming a personal project, but hey that's me. Towards 1997 I didn't know scheme from elisp and had little idea of what the fuss was all about. I was aware of Guile as a half-way done project in its own right (as of 2004 you could say it's still half-way done: some parts have advanced and some parts have regressed) and because I support the goals of the Free Software Foundation, decided to use it as the basis for my personal cultivation.
Learning is a process that converts time and effort into a progression of improvements in understanding and skill — so hopes the learner! Here's a list of modules, sorted by initial checkin time (oldest first), accompanied by occasional commentary on the evolution of the code and/or interface, as a manifestation of what I have learned. The second date is the last modification time, and the number of revisions is in parentheses.
It's easier to remove object properties from our own conses than from a mixed set. Extra effort to avoid client data contamination is worth it.
Better to use tmpfile than tmpnam.
Using file descriptors is easy!
Keywords are easier than symbols to visually distinguish.
What is "current" needs to be well-understood to be well-defined.
Functional style is great but it isn't everything. This is the first module to support caller-controlled memory-management, so we'll see how that style fares in the long run.
Can define neither map nor -i because those are fundamental.
So, to make things “easier”, we require typing ~ all the time,
every time...
If this section has bored you to death you should at least take away the important message: learning scheme programming is up to you; in practice the practice of others is less useful to hear about than in theory!
To indulge in some light-hearted Verilog envy, I started the THUD project1; and from there pulled the first few modules for ttn-pers-scheme:
echo and echo -n equivalents
for both write and display
These modules, being among the very first, use very simple constructs
befitting a beginning programmer. To date,
shell-command-to-string is still used in various
places, while the others have largely gone neglected. THUD itself has
been languishing (until recently) due to lack of attention (not to mention
being largely overshadowed by the excellent SIMSYNCH package2 by Aubrey
Jaffer, author of SCM and hence the progenitor of Guile). C'ést la vie.
Somewhat (but not entirely, I note gratefully) consistent with the concept of things growing (see Learning Scheme Programming) and falling down (see THUD), I enjoyed a bit of physical motion through space for a certain period of time, and began keeping notes of my ponderings in the midst of fearsome bouts of intense two-wheeled concentration, publishing these thoughts on the web as a celebration of survival.3
As no doubt many other dilettantes have done before me, I originally used only Emacs 20 and a bit of elisp on a text-only laptop, to update static pages on the website via a 28.8 modem. This suited me fine for several years, but over time, a mild curiosity about the fancy doodads deprived of this lynx (now w3m) user coupled with a growing interest in the dynamic nature of everything, led me away from the template-filling mindset and towards the tree-walking one.
The modules in ttn-pers-scheme developed in support of this wanderlust fall into a few broad categories.
filesystem-tree-to-list,
(ttn collect-files-sorted-by-mtime),
dirutils,
fileutils,
expand-file-name,
(ttn ftw).
(ttn gap-buffer),
bufutils,
make-buffered-caller.
(ttn pgmeta),
(ttn pgtable),
(ttn pgtype),
(ttn psql),
(ttn display-table).
It turns out website maintenance involves lots of interesting details, even after the wanderlust has subsided. Note that some modules have left the package (see Learning to Let Go), leaving only their names.
Many times, the act of expressing oneself, whether it be in prose, poetry, or structured expressions, is of momentary fulfillment; the value lies more in the act and less in the artifact. This section describes some of the modules retired from ttn-pers-scheme for one reason or another.
[In fact, all of ttn-pers-scheme is going away! See Future.]
(ttn psql)ttn-do package:
http://www.gnuvola.org/software/ttn-do/
but after a brief stay, migrated to the Guile-PG package:
http://www.gnuvola.org/software/guile-pg/
where it presently resides (and continues to evolve)
as module (database postgres-gxrepl).
(ttn ftw)(ice-9 ftw). At one point
the code was ready to be added to SLIB, but I let the docs side slide
for a few years. When the docs were finally ready, I balked at having
to place the code and docs in the public domain, a prerequisite for SLIB
inclusion (if I recall correctly). Perhaps that will come to pass,
still, but it won't be mentioned here any longer.
(ttn eformat)(ice-9 infix), initially
written by Keisuke Nishida and posted to the mailing lists without a
copyright notice around December 2000. I “rescued” it from the public
domain so it's only fitting that it freely slide back into that space
after some years of inactivity.
(ttn gap-buffer)(ice-9 gap-buffer).
Another lesson, no longer captured automatically (see Learning Scheme Programming) since the module's departure, but worth mentioning
here anyway, is that the naive implementation with two strings was both
incredibly easy to write and incredibly slow; thus, better to have
invested design time up front.
(ttn pgtype)(ttn pgtable)(ttn pgmeta)(ttn display-table)(ttn psql), these modules have migrated to, and continue to
evolve in, the Guile-PG package. Their names there are
(database postgres-types),
(database postgres-table),
(database postgres-meta) and
(database postgres-resdisp), respectively.
(ttn collect-files-sorted-by-mtime)(ttn defvar)(ttn edit)(ice-9 editing-buffer), with
compatible interface and enhanced functionality. Here is the “lesson”
of this module:
Non-hygenic macros are more useful than hygenic in this case, since we want environment capture in addition to simple rewriting.
Now, who knows what that really means?!
(ttn flatten-to-buffer)(ttn find-file)(ttn save-buffer)(ttn write-buffer-if-changed)(ttn write-buffer)As of version 0.50, ttn-pers-scheme is obsoleted by ttn-do, (and other packages) and will no longer be available after 2008-12-31.
The following table indicates where to find equivalent (or, in many cases, extended and improved) functionality for ‘(ttn foo)’ modules:
On the other hand, these are modules whose functionality did not make it out of the sinking ship:
bufutils
email-log-if-fail
filesystem-tree-to-list
html-data
listener
sanity
scat
slackful-concat
Here is a list of concepts, procedures and data structures described or mentioned in this manual:
*format-changes*: sgf*properties*: sgf:DQ: markup:LF: markup:NULL: markupab-children: abbrev-treeab-children-set!: abbrev-treeab-data: abbrev-treeab-data-set!: abbrev-treeab-fullname: abbrev-treeab?: abbrev-treeaba-ab: abbrev-treeaba-alias: abbrev-treeaba?: abbrev-treeabbr: html-dataabtree-add: abbrev-treeabtree-climb: abbrev-treeabtree-expand: abbrev-treeabtree-import: abbrev-treeadd-to-load-path!: sanityanalyze: sgfargs->normalized-list: shellutilsb: html-datablockquote: html-databody: html-databr: html-datacall-process: call-processcall-process->buffers: call-process-to-bufferscall-process-to-buffers: call-process-to-bufferscenter: html-datachildren: sgfcite: html-dataclearsign-message-interactively: gpgutilscode: html-datacopyright: html-datacopyright-since: html-datacron: croncron!: croncss-tree: markupcvs-files-under: cvsutils