xcode.icn: Procedures to save and restore Icon data

procedure xencode:         write structure to file
procedure xdecode:         read structure from file

link xcode
November 19, 1997; Bob Alexander
Contributor: Ralph E. Griswold
See also: codeobj.icn
This file is in the public domain.

Description
-----------

   These procedures provide a way of storing Icon values in files
and retrieving them.  The procedure xencode(x,f) stores x in file f
such that it can be converted back to x by xdecode(f).  These
procedures handle several kinds of values, including structures of
arbitrary complexity and even loops.  The following sequence will
output x and recreate it as y:

     f := open("xstore","w")
     xencode(x,f)
     close(f)
     f := open("xstore")
     y := xdecode(f)
     close(f)

For "scalar" types -- null, integer, real, cset, and string, the
above sequence will result in the relationship

     x === y

   For structured types -- list, set, table, and record types --
y is, for course, not identical to x, but it has the same "shape" and
its elements bear the same relation to the original as if they were
encoded and decoded individually.

   Files, co-expressions, and windows cannot generally be restored in any
way that makes much sense.  These objects are restored as empty lists so
that (1) they will be unique objects and (2) will likely generate
run-time errors if they are (probably erroneously) used in
computation.  However, the special files &input, &output, and &errout are
restored.

   Not much can be done with functions and procedures, except to preserve
type and identification.

   The encoding of strings and csets handles all characters in a way
that it is safe to write the encoding to a file and read it back.

   xdecode() fails if given a file that is not in xcode format or it
the encoded file contains a record for which there is no declaration
in the program in which the decoding is done.  Of course, if a record
is declared differently in the encoding and decoding programs, the
decoding may be bogus.

   xencoden() and xdecoden() perform the same operations, except
xencoden() and xdecoden() take the name of a file, not a file.

   xencodet() and xdecodet() are like xencode() and xdecode()
except that the trailing argument is a type name.  If the encoded
decoded value is not of that type, they fail.  xencodet() does
not take an opt argument.
____________________________________________________________

Complete calling sequences
--------------------------

     xencode(x, f, p) # returns f

     where

             x is the object to encode

             f is the file to write (default &output)

             p is a procedure that writes a line on f using the
               same interface as write() (the first parameter is
               always a the value passed as "file") (default: write)


     xencode(f, p) # returns the restored object

     where

             f is the file to read (default &input)

             p is a procedure that reads a line from f using the
               same interface as read() (the parameter is
               always a the value passed as "file") (default: read)


The "p" parameter is not normally used for storage in text files, but
it provides the flexibility to store the data in other ways, such as
a string in memory.  If "p" is provided, then "f" can be any
arbitrary data object -- it need not be a file.

For example, to "write" x to an Icon string:

     record StringFile(s)

     procedure main()
        ...
        encodeString := xencode(x,StringFile(""),WriteString).s
        ...
     end

     procedure WriteString(f,s[])
       every f.s ||:= !s
       f.s ||:= "\n"
       return
     end
____________________________________________________________

Notes on the encoding
---------------------

   Values are encoded as a sequence of one or more lines written to
a plain text file.  The first or only line of a value begins with a
single character that unambiguously indicates its type.  The
remainder of the line, for some types, contains additional value
information.  Then, for some types, additional lines follow
consisting of additional object encodings that further specify the
object.  The null value is a special case consisting of an empty
line.

   Each object other than &null is assigned an integer tag as it is
encoded.  The tag is not, however, written to the output file.  On
input, tags are assigned in the same order as objects are decoded, so
each restored object is associated with the same integer tag as it
was when being written.  In encoding, any recurrence of an object is
represented by the original object's tag.  Tag references are
represented as integers, and are easily recognized since no object's
representation begins with a digit.

   Where a structure contains elements, the encodings of the
elements follow the structure's specification on following lines.
Note that the form of the encoding contains the information needed to
separate consecutive elements.

   Here are some examples of values and their encodings:

     x                     encode(x)
-------------------------------------------------------

     1                     N1
     2.0                   N2.0
     &null
     "\377"                "\377"
     '\376\377'            '\376\377'
     procedure main        p
                           "main"
     co-expression #1 (0)  C
     []                    L
                           N0
     set()                 "S"
                           N0
     table("a")            T
                           N0
                           "a"
     ["hi","there"]        L
                           N2
                           "hi"
                           "there"

A loop is illustrated by

     L2 := []
     put(L2,L2)

for which

     x                     encode(x)
-------------------------------------------------------

     L2                    L
                           N1
                           2

The "2" on the third line is a tag referring to the list L2.  The tag
ordering specifies that an object is tagged *after* its describing
objects, thus the list L2 has the tag 2 (the integer 1 has tag 1).

   Of course, you don't have to know all this to use xencode and
xdecode.

Source code | Program Library Page | Icon Home Page