   This file documents the MIT Scheme system.

   Copyright (C) 1988-2002 Massachusetts Institute of Technology

   Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.1 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts.  A copy of the license is included in the section entitled "GNU
Free Documentation License".

*Matcher
--------

   The "matcher language" is a declarative language for specifying a
"matcher procedure".  A matcher procedure is a procedure that accepts a
single parser-buffer argument and returns a boolean value indicating
whether the match it performs was successful.  If the match succeeds,
the internal pointer of the parser buffer is moved forward over the
matched text.  If the match fails, the internal pointer is unchanged.

   For example, here is a matcher procedure that matches the character
`a':

     (lambda (b) (match-parser-buffer-char b #\a))

Here is another example that matches two given characters, C1 and C2,
in sequence:

     (lambda (b)
       (let ((p (get-parser-buffer-pointer b)))
         (if (match-parser-buffer-char b C1)
             (if (match-parser-buffer-char b C2)
                 #t
                 (begin
                   (set-parser-buffer-pointer! b p)
                   #f))
             #f)))

This is code is clear, but has lots of details that get in the way of
understanding what it is doing.  Here is the same example in the
matcher language:

     (*matcher (seq (char C1) (char C2)))

This is much simpler and more intuitive.  And it generates virtually
the same code:

     (pp (*matcher (seq (char c1) (char c2))))
     -| (lambda (#[b1])
     -|   (let ((#[p1] (get-parser-buffer-pointer #[b1])))
     -|     (and (match-parser-buffer-char #[b1] c1)
     -|          (if (match-parser-buffer-char #[b1] c2)
     -|              #t
     -|              (begin
     -|                (set-parser-buffer-pointer! #[b1] #[p1])
     -|                #f)))))

   Now that we have seen an example of the language, it's time to look
at the detail.  The `*matcher' special form is the interface between
the matcher language and Scheme.

 - special form: *matcher mexp
     The operand MEXP is an expression in the matcher language.  The
     `*matcher' expression expands into Scheme code that implements a
     matcher procedure.

   Here are the predefined matcher expressions.  New matcher expressions
can be defined using the macro facility (see Parser-language Macros).
We will start with the primitive expressions.

 - matcher expression: char expression
 - matcher expression: char-ci expression
 - matcher expression: not-char expression
 - matcher expression: not-char-ci expression
     These expressions match a given character.  In each case, the
     EXPRESSION operand is a Scheme expression that must evaluate to a
     character at run time.  The `-ci' expressions do case-insensitive
     matching.  The `not-' expressions match any character other than
     the given one.

 - matcher expression: string expression
 - matcher expression: string-ci expression
     These expressions match a given string.  The EXPRESSION operand is
     a Scheme expression that must evaluate to a string at run time.
     The `string-ci' expression does case-insensitive matching.

 - matcher expression: char-set expression
     These expressions match a single character that is a member of a
     given character set.  The EXPRESSION operand is a Scheme expression
     that must evaluate to a character set at run time.

 - matcher expression: alphabet expression
     These expressions match a single character that is a member of a
     given Unicode alphabet (see Unicode).  The EXPRESSION operand
     is a Scheme expression that must evaluate to an alphabet at run
     time.

 - matcher expression: end-of-input
     The `end-of-input' expression is successful only when there are no
     more characters available to be matched.

 - matcher expression: discard-matched
     The `discard-matched' expression always successfully matches the
     null string.  However, it isn't meant to be used as a matching
     expression; it is used for its effect.  `discard-matched' causes
     all of the buffered text prior to this point to be discarded (i.e.
     it calls `discard-parser-buffer-head!' on the parser buffer).

     Note that `discard-matched' may not be used in certain places in a
     matcher expression.  The reason for this is that it deliberately
     discards information needed for backtracking, so it may not be
     used in a place where subsequent backtracking will need to back
     over it.  As a rule of thumb, use `discard-matched' only in the
     last operand of a `seq' or `alt' expression (including any `seq' or
     `alt' expressions in which it is indirectly contained).

   In addition to the above primitive expressions, there are two
convenient abbreviations.  A character literal (e.g. `#\A') is a legal
primitive expression, and is equivalent to a `char' expression with
that literal as its operand (e.g. `(char #\A)').  Likewise, a string
literal is equivalent to a `string' expression (e.g. `(string "abc")').

   Next there are several combinator expressions.  These closely
correspond to similar combinators in regular expressions.  Parameters
named MEXP are arbitrary expressions in the matcher language.

 - matcher expression: seq mexp ...
     This matches each MEXP operand in sequence.  For example,

          (seq (char-set char-set:alphabetic)
               (char-set char-set:numeric))

     matches an alphabetic character followed by a numeric character,
     such as `H4'.

     Note that if there are no MEXP operands, the `seq' expression
     successfully matches the null string.

 - matcher expression: alt mexp ...
     This attempts to match each MEXP operand in order from left to
     right.  The first one that successfully matches becomes the match
     for the entire `alt' expression.

     The `alt' expression participates in backtracking.  If one of the
     MEXP operands matches, but the overall match in which this
     expression is embedded fails, the backtracking mechanism will cause
     the `alt' expression to try the remaining MEXP operands.  For
     example, if the expression

          (seq (alt "ab" "a") "b")

     is matched against the text `abc', the `alt' expression will
     initially match its first operand.  But it will then fail to match
     the second operand of the `seq' expression.  This will cause the
     `alt' to be restarted, at which time it will match `a', and the
     overall match will succeed.

     Note that if there are no MEXP operands, the `alt' match will
     always fail.

 - matcher expression: * mexp
     This matches zero or more occurrences of the MEXP operand.
     (Consequently this match always succeeds.)

     The `*' expression participates in backtracking; if it matches N
     occurrences of MEXP, but the overall match fails, it will
     backtrack to N-1 occurrences and continue.  If the overall match
     continues to fail, the `*' expression will continue to backtrack
     until there are no occurrences left.

 - matcher expression: + mexp
     This matches one or more occurrences of the MEXP operand.  It is
     equivalent to

          (seq MEXP (* MEXP))

 - matcher expression: ? mexp
     This matches zero or one occurrences of the MEXP operand.  It is
     equivalent to

          (alt MEXP (seq))

 - matcher expression: sexp expression
     The `sexp' expression allows arbitrary Scheme code to be embedded
     inside a matcher.  The EXPRESSION operand must evaluate to a
     matcher procedure at run time; the procedure is called to match the
     parser buffer.  For example,

          (*matcher
           (seq "a"
                (sexp parse-foo)
                "b"))

     expands to

          (lambda (#[b1])
            (let ((#[p1] (get-parser-buffer-pointer #[b1])))
              (and (match-parser-buffer-char #[b1] #\a)
                   (if (parse-foo #[b1])
                       (if (match-parser-buffer-char #[b1] #\b)
                           #t
                           (begin
                             (set-parser-buffer-pointer! #[b1] #[p1])
                             #f))
                       (begin
                         (set-parser-buffer-pointer! #[b1] #[p1])
                         #f)))))

     The case in which EXPRESSION is a symbol is so common that it has
     an abbreviation: `(sexp SYMBOL)' may be abbreviated as just SYMBOL.

 - matcher expression: with-pointer identifier mexp
     The `with-pointer' expression fetches the parser buffer's internal
     pointer (using `get-parser-buffer-pointer'), binds it to
     IDENTIFIER, and then matches the pattern specified by MEXP.
     IDENTIFIER must be a symbol.

     This is meant to be used on conjunction with `sexp', as a way to
     capture a pointer to a part of the input stream that is outside the
     `sexp' expression.  An example of the use of `with-pointer'
     appears above (see with-pointer example).

*Parser
-------

   The "parser language" is a declarative language for specifying a
"parser procedure".  A parser procedure is a procedure that accepts a
single parser-buffer argument and parses some of the input from the
buffer.  If the parse is successful, the procedure returns a vector of
objects that are the result of the parse, and the internal pointer of
the parser buffer is advanced past the input that was parsed.  If the
parse fails, the procedure returns `#f' and the internal pointer is
unchanged.  This interface is much like that of a matcher procedure,
except that on success the parser procedure returns a vector of values
rather than `#t'.

   The `*parser' special form is the interface between the parser
language and Scheme.

 - special form: *parser pexp
     The operand PEXP is an expression in the parser language.  The
     `*parser' expression expands into Scheme code that implements a
     parser procedure.

   There are several primitive expressions in the parser language.  The
first two provide a bridge to the matcher language (see *Matcher):

 - parser expression: match mexp
     The `match' expression performs a match on the parser buffer.  The
     match to be performed is specified by MEXP, which is an expression
     in the matcher language.  If the match is successful, the result
     of the `match' expression is a vector of one element: a string
     containing that text.

 - parser expression: noise mexp
     The `noise' expression performs a match on the parser buffer.  The
     match to be performed is specified by MEXP, which is an expression
     in the matcher language.  If the match is successful, the result
     of the `noise' expression is a vector of zero elements.  (In other
     words, the text is matched and then thrown away.)

     The MEXP operand is often a known character or string, so in the
     case that MEXP is a character or string literal, the `noise'
     expression can be abbreviated as the literal.  In other words,
     `(noise "foo")' can be abbreviated just `"foo"'.

 - parser expression: values expression ...
     Sometimes it is useful to be able to insert arbitrary values into
     the parser result.  The `values' expression supports this.  The
     EXPRESSION arguments are arbitrary Scheme expressions that are
     evaluated at run time and returned in a vector.  The `values'
     expression always succeeds and never modifies the internal pointer
     of the parser buffer.

 - parser expression: discard-matched
     The `discard-matched' expression always succeeds, returning a
     vector of zero elements.  In all other respects it is identical to
     the `discard-matched' expression in the matcher language.

   Next there are several combinator expressions.  Parameters named
PEXP are arbitrary expressions in the parser language.  The first few
combinators are direct equivalents of those in the matcher language.

 - parser expression: seq pexp ...
     The `seq' expression parses each of the PEXP operands in order.
     If all of the PEXP operands successfully match, the result is the
     concatenation of their values (by `vector-append').

 - parser expression: alt pexp ...
     The `alt' expression attempts to parse each PEXP operand in order
     from left to right.  The first one that successfully parses
     produces the result for the entire `alt' expression.

     Like the `alt' expression in the matcher language, this expression
     participates in backtracking.

 - parser expression: * pexp
     The `*' expression parses zero or more occurrences of PEXP.  The
     results of the parsed occurrences are concatenated together (by
     `vector-append') to produce the expression's result.

     Like the `*' expression in the matcher language, this expression
     participates in backtracking.

 - parser expression: + pexp
     The `*' expression parses one or more occurrences of PEXP.  It is
     equivalent to

          (seq PEXP (* PEXP))

 - parser expression: ? pexp
     The `*' expression parses zero or one occurrences of PEXP.  It is
     equivalent to

          (alt PEXP (seq))

   The next three expressions do not have equivalents in the matcher
language.  Each accepts a single PEXP argument, which is parsed in the
usual way.  These expressions perform transformations on the returned
values of a successful match.

 - parser expression: transform expression pexp
     The `transform' expression performs an arbitrary transformation of
     the values returned by parsing PEXP.  EXPRESSION is a Scheme
     expression that must evaluate to a procedure at run time.  If PEXP
     is successfully parsed, the procedure is called with the vector of
     values as its argument, and must return a vector or `#f'.  If it
     returns a vector, the parse is successful, and those are the
     resulting values.  If it returns `#f', the parse fails and the
     internal pointer of the parser buffer is returned to what it was
     before PEXP was parsed.

     For example:

          (transform (lambda (v) (if (= 0 (vector-length v)) #f v)) ...)

 - parser expression: encapsulate expression pexp
     The `encapsulate' expression transforms the values returned by
     parsing PEXP into a single value.  EXPRESSION is a Scheme
     expression that must evaluate to a procedure at run time.  If PEXP
     is successfully parsed, the procedure is called with the vector of
     values as its argument, and may return any Scheme object.  The
     result of the `encapsulate' expression is a vector of length one
     containing that object.  (And consequently `encapsulate' doesn't
     change the success or failure of PEXP, only its value.)

     For example:

          (encapsulate vector->list ...)

 - parser expression: map expression pexp
     The `map' expression performs a per-element transform on the
     values returned by parsing PEXP.  EXPRESSION is a Scheme
     expression that must evaluate to a procedure at run time.  If PEXP
     is successfully parsed, the procedure is mapped (by `vector-map')
     over the values returned from the parse.  The mapped values are
     returned as the result of the `map' expression.  (And consequently
     `map' doesn't change the success or failure of PEXP, nor the
     number of values returned.)

     For example:

          (map string->symbol ...)

   Finally, as in the matcher language, we have `sexp' and
`with-pointer' to support embedding Scheme code in the parser.

 - parser expression: sexp expression
     The `sexp' expression allows arbitrary Scheme code to be embedded
     inside a parser.  The EXPRESSION operand must evaluate to a parser
     procedure at run time; the procedure is called to parse the parser
     buffer.  This is the parser-language equivalent of the `sexp'
     expression in the matcher language.

     The case in which EXPRESSION is a symbol is so common that it has
     an abbreviation: `(sexp SYMBOL)' may be abbreviated as just SYMBOL.

 - parser expression: with-pointer identifier pexp
     The `with-pointer' expression fetches the parser buffer's internal
     pointer (using `get-parser-buffer-pointer'), binds it to
     IDENTIFIER, and then parses the pattern specified by PEXP.
     IDENTIFIER must be a symbol.  This is the parser-language
     equivalent of the `with-pointer' expression in the matcher
     language.

Parser-language Macros
----------------------

   The parser and matcher languages provide a macro facility so that
common patterns can be abstracted.  The macro facility allows new
expression types to be independently defined in the two languages.  The
macros are defined in heirarchically organized tables, so that
different applications can have private macro bindings.

 - special form: define-*matcher-macro formals expression
 - special form: define-*parser-macro formals expression
     These special forms are used to define macros in the matcher and
     parser language, respectively.  FORMALS is like the FORMALS list
     of a `define' special form, and EXPRESSION is a Scheme expression.

     If FORMALS is a list (or improper list) of symbols, the first
     symbol in the list is the name of the macro, and the remaining
     symbols are interpreted as the FORMALS of a lambda expression.  A
     lambda expression is formed by combining the latter FORMALS with
     the EXPRESSION, and this lambda expression, when evaluated, becomes
     the "expander".  The defined macro accepts the same number of
     operands as the expander.  A macro instance is expanded by applying
     the expander to the list of operands; the result of the
     application is interpreted as a replacement expression for the
     macro instance.

     If FORMALS is a symbol, it is the name of the macro.  In this
     case, the expander is a procedure of no arguments whose body is
     EXPRESSION.  When the FORMALS symbol appears by itself as an
     expression in the language, the expander is called with no
     arguments, and the result is interpreted as a replacement
     expression for the symbol.

 - procedure: define-*matcher-expander identifier expander
 - procedure: define-*parser-expander identifier expander
     These procedures provide a procedural interface to the
     macro-definition mechanism.  IDENTIFIER must be a symbol, and
     EXPANDER must be an expander procedure, as defined above.
     Instances of the `define-*matcher-macro' and
     `define-*parser-macro' special forms expand into calls to these
     procedures.

   The remaining procedures define the interface to the parser-macros
table abstraction.  Each parser-macro table has a separate binding
space for macros in the matcher and parser languages.  However, the
table inherits bindings from one specified table; it's not possible to
inherit matcher-language bindings from one table and parser-language
bindings from another.

 - procedure: make-parser-macros parent-table
     Create and return a new parser-macro table that inherits from
     PARENT-TABLE.  PARENT-TABLE must be either a parser-macro table,
     or `#f'; usually it is specified as the value of
     `global-parser-macros'.

 - procedure: parser-macros? object
     This is a predicate for parser-macro tables.

 - procedure: global-parser-macros
     Return the global parser-macro table.  This table is predefined and
     contains all of the bindings documented here.

   There is a "current" table at all times, and macro definitions are
always placed in this table.  By default, the current table is the
global macro table, but the following procedures allow this to be
changed.

 - procedure: current-parser-macros
     Return the current parser-macro table.

 - procedure: set-current-parser-macros! table
     Change the current parser-macro table to TABLE, which must satisfy
     `parser-macros?'.

 - procedure: with-current-parser-macros table thunk
     Bind the current parser-macro table to TABLE, call THUNK with no
     arguments, then restore the original table binding.  The value
     returned by THUNK is the returned as the value of this procedure.
     TABLE must satisfy `parser-macros?', and THUNK must be a procedure
     of no arguments.

XML Parser
==========

   MIT Scheme provides a simple non-validating XML parser.  This parser
is mostly conformant, with the exception that it doesn't support
UTF-16.  The parser also does not support external document type
declarations (DTDs).  The output of the parser is a record tree that
closely reflects the structure of the XML document.

   There is also an output mechanism that writes an XML record tree to
a port.  There is no guarantee that parsing an XML document and writing
it back out will make a verbatim copy of the document.  The output will
be semantically identical but may have small syntactic differences.
For example, comments are discarded by the parser, and entities are
substituted during the parsing process.

   The purpose of the XML support is to provide a mechanism for reading
and writing simple XML documents.  In the future this support may be
further developed to support a standard interface such as DOM or SAX.

   The XML support is a run-time-loadable option; to use it, execute

     (load-option 'xml)

once before compiling any code that uses it.

   The XML interface consists of an input procedure, an output
procedure, and a set of record types.

 - procedure: parse-xml-document buffer
     This procedure parses an XML input stream and returns a
     newly-allocated XML record tree.  The BUFFER argument must be a
     parser buffer (see Parser Buffers).  Most errors in the input
     stream are detected and signalled, with information identifying
     the location of the error where possible.  Note that the input
     stream is assumed to be UTF-8.

 - procedure: write-xml xml-document port
     This procedure writes an XML record tree to PORT.  The
     XML-DOCUMENT argument must be a record of type `xml-document',
     which is the root record of an XML record tree.  The output is
     encoded in UTF-8.

   XML names are represented in memory as symbols.  All symbols
appearing within XML records are XML names.  Because XML names are case
sensitive, there is a procedure to intern these symbols:

 - procedure: xml-intern string
     Returns the XML name called STRING.  XML names are represented as
     symbols, but unlike ordinary Scheme symbols, they are case
     sensitive.  The following is true for any two strings STRING1 and
     STRING2:

          (let ((name1 (xml-intern STRING1))
                (name2 (xml-intern STRING2)))
            (if (string=? STRING1 STRING2)
                (eq? name1 name2)
                (not (eq? name1 name2))))

   The output from the XML parser and the input to the XML output
procedure is a complex data structure composed of a heirarchy of typed
components.  Each component is a record whose fields correspond to
parts of the XML structure that the record represents.  There are no
special operations on these records; each is a tuple with named
subparts.  The root record type is `xml-document', which represents a
complete XML document.

   Each record type TYPE has the following associated bindings:

`TYPE-rtd'
     is a variable bound to the record-type descriptor for TYPE.  The
     record-type descriptor may be used as a specializer in SOS method
     definitions, which greatly simplifies code to dispatch on these
     types.

`TYPE?'
     is a predicate for records of type TYPE.  It accepts one argument,
     which can be any object, and returns `#t' if the object is a
     record of this type, or `#f' otherwise.

`make-TYPE'
     is a constructor for records of type TYPE.  It accepts one
     argument for each field of TYPE, in the same order that they are
     written in the type description, and returns a newly-allocated
     record of that type.

`TYPE-FIELD'
     is an accessor procedure for the field FIELD in records of type
     TYPE.  It accepts one argument, which must be a record of that
     type, and returns the contents of the corresponding field in the
     record.

`set-TYPE-FIELD!'
     is a modifier procedure for the field FIELD in records of type
     TYPE.  It accepts two arguments: the first must be a record of
     that type, and the second is a new value for the corresponding
     field.  The record's field is modified to have the new value.

 - record type: xml-document declaration misc-1 dtd misc-2 root misc-3
     The `xml-document' record is the top-level record representing a
     complete XML document.  DECLARATION is either an `xml-declaration'
     object or `#f'.  DTD is either an `xml-dtd' object or `#f'.  ROOT
     is an `xml-element' object.  MISC-1, MISC-2, and MISC-3 are lists
     of miscellaneous items; a miscellaneous item is either an
     `xml-processing-instructions' object or a string of whitespace.

 - record type: xml-declaration version encoding standalone
     The `xml-declaration' record represents the `<?xml ...  ?>'
     declaration that optionally appears at the beginning of an XML
     document.  VERSION is a version string, typically `"1.0"'.
     ENCODING is either an encoding string or `#f'.  STANDALONE is
     either `"yes"', `"no"', or `#f'.

 - record type: xml-element name attributes contents
     The `xml-element' record represents general XML elements; the bulk
     of a typical XML document consists of these elements.  NAME is the
     element name (a symbol).  ATTRIBUTES is a list of attributes; each
     attribute is a pair whose CAR is the attribute name (a symbol),
     and whose CDR is the attribute value (a string).  CONTENTS is a
     list of the contents of the element.  Each element of this list is
     either a string, an `xml-element' record, an
     `xml-processing-instructions' record, or an `xml-uninterpreted'
     record.

 - record type: xml-processing-instructions name text
     The `xml-processing-instructions' record represents processing
     instructions, which have the form `<?NAME ... ?>'.  These
     instructions are intended to contain non-XML data that will be
     processed by another interpreter; for example they might contain
     PHP programs.  The NAME field is the processor name (a symbol),
     and the TEXT field is the body of the instructions (a string).

 - record type: xml-uninterpreted text
     Some documents contain entity references that can't be expanded by
     the parser, perhaps because the document requires an external DTD.
     Such references are left uninterpreted in the output by wrapping
     them in `xml-uninterpreted' records.  In some situations, for
     example when they are embedded in attribute values, the
     surrounding text is also included in the `xml-uninterpreted'
     record.  The TEXT field contains the uninterpreted XML text (a
     string).

 - record type: xml-dtd root external internal
     The `xml-dtd' record represents a document type declaration.  The
     ROOT field is an XML name for the root element of the document.
     EXTERNAL is either an `xml-external-id' record or `#f'.  INTERNAL
     is a list of DTD element records (e.g. `xml-!element',
     `xml-!attlist', etc.).

   The remaining record types are valid only within a DTD.

 - record type: xml-!element name content-type
     The `xml-!element' record represents an element-type declaration.
     NAME is the XML name of the type being declared (a symbol).
     CONTENT-TYPE describes the type and can have several different
     values, as follows:

        * The XML names `EMPTY' and `ANY' correspond to the XML
          keywords of the same name.

        * A list `(MIX TYPE ...)' corresponds to the `(#PCDATA | TYPE |
          ...)' syntax.

 - record type: xml-!attlist name definitions
     The `xml-!attlist' record represents an attribute-list
     declaration.  NAME is the XML name of the type for which
     attributes are being declared (a symbol).  DEFINITIONS is a list
     of attribute definitions, each of which is a list of three
     elements `(NAME TYPE DEFAULT)'.  NAME is an XML name for the name
     of the attribute (a symbol).  TYPE describes the attribute type,
     and can have one of the following values:

        * The XML names `CDATA', `IDREFS', `IDREF', `ID', `ENTITY',
          `ENTITIES', `NMTOKENS', and `NMTOKEN' correspond to the XML
          keywords of the same names.

        * A list `(NOTATION NAME1 NAME2 ...)' corresponds to the
          `NOTATION (NAME1 | NAME2 ...)' syntax.

        * A list `(ENUMERATED NAME1 NAME2 ...)' corresponds to the
          `(NAME1 | NAME2 ...)' syntax.

     DEFAULT describes the default value for the attribute, and can
     have one of the following values:

        * The XML names `#REQUIRED' and `#IMPLIED' correspond to the
          XML keywords of the same names.

        * A list `(#FIXED VALUE)' corresponds to the `#FIXED "VALUE"'
          syntax.  VALUE is represented as a string, but might also be
          an `xml-uninterpreted' record.

        * A list `(DEFAULT VALUE)' corresponds to the `"VALUE"' syntax.
          VALUE is represented as a string, but might also be an
          `xml-uninterpreted' record.

 - record type: xml-!entity name value
     The `xml-!entity' record represents a general entity declaration.
     NAME is an XML name for the entity.  VALUE is the entity's value,
     either a string, an `xml-uninterpreted' record, or an
     `xml-external-id' record.

 - record type: xml-parameter-!entity name value
     The `xml-parameter-!entity' record represents a parameter entity
     declaration.  NAME is an XML name for the entity.  VALUE is the
     entity's value, either a string, an `xml-uninterpreted' record, or
     an `xml-external-id' record.

 - record type: xml-unparsed-!entity name id notation
     The `xml-unparsed-!entity' record represents an unparsed entity
     declaration.  `Name' is an XML name for the entity.  ID is an
     `xml-external-id' record.  NOTATION is an XML name for the
     notation.

 - record type: xml-!notation name id
     The `xml-!notation' record represents a notation declaration.
     `Name' is an XML name for the notation.  ID is an
     `xml-external-id' record.

 - record type: xml-external-id id uri
     The `xml-external-id' record is a reference to an external DTD.
     This reference consists of two parts: ID is a public ID literal,
     corresponding to the `PUBLIC' keyword, while URI is a system
     literal, corresponding to the `SYSTEM' keyword.  Either or both
     may be present, depending on the context.  Each is represented as
     a string.

Operating-System Interface
**************************

   The Scheme standard provides a simple mechanism for reading and
writing files: file ports.  MIT Scheme provides additional tools for
dealing with other aspects of the operating system:

   * "Pathnames" are a reasonably operating-system independent tool for
     manipulating the component parts of file names.  This can be
     useful for implementing defaulting of file name components.

   * Control over the "current working directory": the place in the file
     system from which relative file names are interpreted.

   * Procedures that rename, copy, delete, and test for the existence of
     files.  Also, procedures that return detailed information about a
     particular file, such as its type (directory, link, etc.) or
     length.

   * Procedures for reading the contents of a directory.

   * Procedures for obtaining times in various formats, converting
     between the formats, and generating human-readable time strings.

   * Procedures to run other programs as subprocesses of Scheme, to read
     their output, and write input to them.

   * A means to determine the operating system Scheme is running under.

Pathnames
=========

   MIT Scheme programs need to use names to designate files.  The main
difficulty in dealing with names of files is that different file systems
have different naming formats for files.  For example, here is a table
of several file systems (actually, operating systems that provide file
systems) and what equivalent file names might look like for each one:

     System          File Name
     ------          ---------
     TOPS-20         <LISPIO>FORMAT.FASL.13
     TOPS-10         FORMAT.FAS[1,4]
     ITS             LISPIO;FORMAT FASL
     MULTICS         >udd>LispIO>format.fasl
     TENEX           <LISPIO>FORMAT.FASL;13
     VAX/VMS         [LISPIO]FORMAT.FAS;13
     UNIX            /usr/lispio/format.fasl
     DOS             C:\USR\LISPIO\FORMAT.FAS

   It would be impossible for each program that deals with file names to
know about each different file name format that exists; a new operating
system to which Scheme was ported might use a format different from any
of its predecessors.  Therefore, MIT Scheme provides _two_ ways to
represent file names: "filenames" (also called "namestrings"), which
are strings in the implementation-dependent form customary for the file
system, and "pathnames", which are special abstract data objects that
represent file names in an implementation-independent way.  Procedures
are provided to convert between these two representations, and all
manipulations of files can be expressed in machine-independent terms by
using pathnames.

   In order to allow MIT Scheme programs to operate in a network
environment that may have more than one kind of file system, the
pathname facility allows a file name to specify which file system is to
be used.  In this context, each file system is called a "host", in
keeping with the usual networking terminology.(1)

   Note that the examples given in this section are specific to unix
pathnames.  Pathnames for other operating systems have different
external representations.

   ---------- Footnotes ----------

   (1) This introduction is adapted from `Common Lisp, The Language',
second edition, section 23.1.

Filenames and Pathnames
-----------------------

   Pathname objects are usually created by parsing filenames (character
strings) into component parts.  MIT Scheme provides operations that
convert filenames into pathnames and vice versa.

 - procedure: ->pathname object
     Returns a pathname that is the equivalent of OBJECT.  OBJECT must
     be a pathname or a string.  If OBJECT is a pathname, it is
     returned.  If OBJECT is a string, this procedure returns the
     pathname that corresponds to the string; in this case it is
     equivalent to `(parse-namestring OBJECT #f #f)'.

          (->pathname "foo")          =>  #[pathname 65 "foo"]
          (->pathname "/usr/morris")  =>  #[pathname 66 "/usr/morris"]

 - procedure: parse-namestring thing [host [defaults]]
     This turns THING into a pathname.  THING must be a pathname or a
     string.  If THING is a pathname, it is returned.  If THING is a
     string, this procedure returns the pathname that corresponds to the
     string, parsed according to the syntax of the file system
     specified by HOST.

     This procedure _does not_ do defaulting of pathname components.

     The optional arguments are used to determine what syntax should be
     used for parsing the string.  In general this is only really
     useful if your implementation of MIT Scheme supports more than one
     file system, otherwise you would use `->pathname'.  If given, HOST
     must be a host object or `#f', and DEFAULTS must be a pathname.
     HOST specifies the syntax used to parse the string.  If HOST is
     not given or `#f', the host component from DEFAULTS is used
     instead; if DEFAULTS is not given, the host component from
     `*default-pathname-defaults*' is used.

 - procedure: ->namestring pathname
     `->namestring' returns a newly allocated string that is the
     filename corresponding to PATHNAME.
          (->namestring (->pathname "/usr/morris/minor.van"))
               =>  "/usr/morris/minor.van"

 - procedure: pathname-simplify pathname
     Returns a pathname that locates the same file or directory as
     PATHNAME, but is in some sense simpler.  Note that
     `pathname-simplify' might not always be able to simplify the
     pathname, e.g. on unix with symbolic links the directory
     `/usr/morris/../' need not be the same as `/usr/'.  In cases of
     uncertainty the behavior is conservative, returning the original
     or a partly simplified pathname.

          (pathname-simplify "/usr/morris/../morris/dance")
               =>  #[pathname "/usr/morris/dance"]

Components of Pathnames
-----------------------

   A pathname object always has six components, described below.  These
components are the common interface that allows programs to work the
same way with different file systems; the mapping of the pathname
components into the concepts peculiar to each file system is taken care
of by the Scheme implementation.

HOST
     The name of the file system on which the file resides.  In the
     current implementation, this component is always a host object
     that is filled in automatically by the runtime system.  When
     specifying the host component, use either `#f' or the value of the
     variable `local-host'.

DEVICE
     Corresponds to the "device" or "file structure" concept in many
     host file systems: the name of a (logical or physical) device
     containing files.  This component is the drive letter for PC file
     systems, and is unused for unix file systems.

DIRECTORY
     Corresponds to the "directory" concept in many host file systems:
     the name of a group of related files (typically those belonging to
     a single user or project).  This component is always used for all
     file systems.

NAME
     The name of a group of files that can be thought of as
     conceptually the "same" file.  This component is always used for
     all file systems.

TYPE
     Corresponds to the "filetype" or "extension" concept in many host
     file systems.  This says what kind of file this is.  Files with
     the same name but different type are usually related in some
     specific way, such as one being a source file, another the
     compiled form of that source, and a third the listing of error
     messages from the compiler.  This component is currently used for
     all file systems, and is formed by taking the characters that
     follow the last dot in the namestring.

VERSION
     Corresponds to the "version number" concept in many host file
     systems.  Typically this is a number that is incremented every
     time the file is modified.  This component is currently unused for
     all file systems.

   Note that a pathname is not necessarily the name of a specific file.
Rather, it is a specification (possibly only a partial specification) of
how to access a file.  A pathname need not correspond to any file that
actually exists, and more than one pathname can refer to the same file.
For example, the pathname with a version of `newest' may refer to the
same file as a pathname with the same components except a certain
number as the version.  Indeed, a pathname with version `newest' may
refer to different files as time passes, because the meaning of such a
pathname depends on the state of the file system.  In file systems with
such facilities as "links", multiple file names, logical devices, and
so on, two pathnames that look quite different may turn out to address
the same file.  To access a file given a pathname, one must do a
file-system operation such as `open-input-file'.

   Two important operations involving pathnames are "parsing" and
"merging".  Parsing is the conversion of a filename (which might be
something supplied interactively  by the users when asked to supply the
name of a file) into a pathname object.  This operation is
implementation-dependent, because the format of filenames is
implementation-dependent.  Merging takes a pathname with missing
components and supplies values for those components from a source of
default values.

   Not all of the components of a pathname need to be specified.  If a
component of a pathname is missing, its value is `#f'.  Before the file
system interface can do anything interesting with a file, such as
opening the file, all the missing components of a pathname must be
filled in.  Pathnames with missing components are used internally for
various purposes; in particular, parsing a namestring that does not
specify certain components will result in a pathname with missing
components.

   Any component of a pathname may be the symbol `unspecific', meaning
that the component simply does not exist, for file systems in which such
a value makes no sense.  For example, unix, Windows, and OS/2 file
systems usually do not support version numbers, so the version component
for such a host might be `unspecific'.(1)

   In addition to `#f' and `unspecific', the components of a pathname
may take on the following meaningful values:

HOST
     An implementation-defined type which may be tested for using the
     `host?' predicate.

DEVICE
     On systems that support this component (Windows and OS/2), it may
     be specified as a string containing a single alphabetic character,
     for which the alphabetic case is ignored.

DIRECTORY
     A non-empty list, which represents a "directory path": a sequence
     of directories, each of which has a name in the previous
     directory, the last of which is the directory specified by the
     entire path.  Each element in such a path specifies the name of
     the directory relative to the directory specified by the elements
     to its left.  The first element of the list is either the symbol
     `absolute' or the symbol `relative'.  If the first element in the
     list is the symbol `absolute', then the directory component (and
     subsequently the pathname) is "absolute"; the first component in
     the sequence is to be found at the "root" of the file system.  If
     the directory is "relative" then the first component is to be
     found in some as yet unspecified directory; typically this is
     later specified to be the "current working directory".

     Aside from `absolute' and `relative', which may only appear as the
     first element of the list, each subsequent element in the list is
     either: a string, which is a literal component; the symbol `wild',
     meaningful only when used in conjunction with the directory
     reader; or the symbol `up', meaning the next directory is the
     "parent" of the previous one.  `up' corresponds to the file `..'
     in unix and PC file systems.

     (The following note does not refer to any file system currently
     supported by MIT Scheme, but is included for completeness.)  In
     file systems that do not have "hierarchical" structure, a specified
     directory component will always be a list whose first element is
     `absolute'.  If the system does not support directories other than
     a single global directory, the list will have no other elements.
     If the system supports "flat" directories, i.e. a global set of
     directories with no subdirectories, then the list will contain a
     second element, which is either a string or `wild'.  In other
     words, a non-hierarchical file system is treated as if it were
     hierarchical, but the hierarchical features are unused.  This
     representation is somewhat inconvenient for such file systems, but
     it discourages programmers from making code depend on the lack of
     a file hierarchy.

NAME
     A string, which is a literal component; or the symbol `wild',
     meaningful only when used in conjunction with the directory reader.

TYPE
     A string, which is a literal component; or the symbol `wild',
     meaningful only when used in conjunction with the directory reader.

VERSION
     An exact positive integer, which is a literal component; the symbol
     `newest', which means to choose the largest available version
     number for that file; the symbol `oldest', which means to choose
     the smallest version number; or the symbol `wild', meaningful only
     when used in conjunction with the directory reader.  In the future
     some other possible values may be added, e.g. `installed'.  Note
     that currently no file systems support version numbers; thus this
     component is not used and should be specified as `#f'.

 - procedure: make-pathname host device directory name type version
     Returns a pathname object whose components are the respective
     arguments.  Each argument must satisfy the restrictions for the
     corresponding component, which were outlined above.

          (make-pathname #f
                         #f
                         '(absolute "usr" "morris")
                         "foo"
                         "scm"
                         #f)
               =>  #[pathname 67 "/usr/morris/foo.scm"]

 - procedure: pathname-host pathname
 - procedure: pathname-device pathname
 - procedure: pathname-directory pathname
 - procedure: pathname-name pathname
 - procedure: pathname-type pathname
 - procedure: pathname-version pathname
     Returns a particular component of PATHNAME.

          (define x (->pathname "/usr/morris/foo.scm"))
          (pathname-host x)       =>  #[host 1]
          (pathname-device x)     =>  unspecific
          (pathname-directory x)  =>  (absolute "usr" "morris")
          (pathname-name x)       =>  "foo"
          (pathname-type x)       =>  "scm"
          (pathname-version x)    =>  unspecific

 - procedure: pathname-new-device pathname device
 - procedure: pathname-new-directory pathname directory
 - procedure: pathname-new-name pathname name
 - procedure: pathname-new-type pathname type
 - procedure: pathname-new-version pathname version
     Returns a new copy of PATHNAME with the respective component
     replaced by the second argument.  PATHNAME is unchanged.  Portable
     programs should not explicitly replace a component with
     `unspecific' because this might not be permitted in some
     situations.

          (define p (->pathname "/usr/blisp/rel15"))
          p
               =>  #[pathname 71 "/usr/blisp/rel15"]
          (pathname-new-name p "rel100")
               =>  #[pathname 72 "/usr/blisp/rel100"]
          (pathname-new-directory p '(relative "test" "morris"))
               =>  #[pathname 73 "test/morris/rel15"]
          p
               =>  #[pathname 71 "/usr/blisp/rel15"]

 - procedure: pathname-default-device pathname device
 - procedure: pathname-default-directory pathname directory
 - procedure: pathname-default-name pathname name
 - procedure: pathname-default-type pathname type
 - procedure: pathname-default-version pathname version
     These operations are similar to the `pathname-new-COMPONENT'
     operations, except that they only change the specified COMPONENT
     if it has the value `#f' in PATHNAME.

   ---------- Footnotes ----------

   (1) This description is adapted from `Common Lisp, The Language',
second edition, section 23.1.1.