\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{\$SPAD/src/algebra files.spad}
\author{Stephen M. Watt, Victor Miller, Barry Trager}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{category FILECAT FileCategory}
<<category FILECAT FileCategory>>=
)abbrev category FILECAT FileCategory
++ Author: Stephen M. Watt, Victor Miller
++ Date Created:
++ Date Last Updated: June 4, 1991
++ Basic Operations:
++ Related Domains: File
++ Also See:
++ AMS Classifications:
++ Keywords:
++ Examples:
++ References:
++ Description:
++   This category provides an interface to operate on files in the
++   computer's file system.  The precise method of naming files
++   is determined by the Name parameter.  The type of the contents
++   of the file is determined by S.

FileCategory(Name, S): Category == FCdefinition where
    Name:      SetCategory
    S:         SetCategory
    IOMode ==> String  -- Union("input", "output", "closed")

    FCdefinition == SetCategory with
        open: Name -> %
          ++ open(s) returns the file s open for input.

        open: (Name, IOMode) -> %
          ++ open(s,mode) returns a file s open for operation in the
          ++ indicated mode: "input" or "output".

        reopen_!: (%, IOMode) -> %
          ++ reopen!(f,mode) returns a file f reopened for operation in the
          ++ indicated mode: "input" or "output".
          ++ \spad{reopen!(f,"input")} will reopen the file f for input.

        close_!: % -> %
          ++ close!(f) returns the file f closed to input and output.

        name: % -> Name
          ++ name(f) returns the external name of the file f.

        iomode: % -> IOMode
          ++ iomode(f) returns the status of the file f. The input/output
          ++ status of f may be "input", "output" or "closed" mode.

        read_!: % -> S
          ++ read!(f) extracts a value from file f.  The state of f is
          ++ modified so a subsequent call to \spadfun{read!} will return
          ++ the next element.

        write_!: (%,S) -> S
          ++ write!(f,s) puts the value s into the file f.
          ++ The state of f is modified so subsequents call to \spad{write!}
          ++ will append one after another.

        flush: % -> Void
            ++ flush(f) make sure that buffered data in written out to
            ++ the operating system.

@

\section{domain FILE File}
<<domain FILE File>>=
)abbrev domain FILE File
++ Author: Stephen M. Watt, Victor Miller
++ Date Created: 1984
++ Date Last Updated: June 4, 1991
++ Basic Operations:
++ Related Domains:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ Examples:
++ References:
++ Description:
++   This domain provides a basic model of files to save arbitrary values.
++   The operations provide sequential access to the contents.

File(S:SetCategory): FileCategory(FileName, S) with
        readIfCan_!: % -> Union(S, "failed")
            ++ readIfCan!(f) returns a value from the file f, if possible.
            ++ If f is not open for reading, or if f is at the end of file
            ++ then \spad{"failed"} is the result.
    == add
        FileState ==> SExpression
        IOMode    ==> String

        Rep:=Record(fileName:    FileName,   _
                    fileState:   FileState,  _
                    fileIOmode:  IOMode)

        defstream(fn: FileName, mode: IOMode): FileState ==
            mode = "input"  =>
              not readable? fn => error ["File is not readable", fn]
              MAKE_-INSTREAM(fn::String)$Lisp
            mode = "output" =>
              not writable? fn => error ["File is not writable", fn]
              MAKE_-OUTSTREAM(fn::String)$Lisp
            error ["IO mode must be input or output", mode]

        f1 = f2 ==
            f1.fileName = f2.fileName
        coerce(f: %): OutputForm ==
            f.fileName::OutputForm

        open fname ==
            open(fname, "input")
        open(fname, mode) ==
            fstream := defstream(fname, mode)
            [fname, fstream, mode]
        reopen_!(f, mode) ==
            fname := f.fileName
            f.fileState := defstream(fname, mode)
            f.fileIOmode:= mode
            f
        close_! f ==
            SHUT(f.fileState)$Lisp
            f.fileIOmode := "closed"
            f
        name f ==
            f.fileName
        iomode f ==
            f.fileIOmode
        read_! f ==
            f.fileIOmode ~= "input" =>
                error "File not in read state"
            x := VMREAD(f.fileState)$Lisp
            PLACEP(x)$Lisp =>
                error "End of file"
            x
        readIfCan_! f ==
            f.fileIOmode ~= "input" =>
                error "File not in read state"
            x: S := VMREAD(f.fileState)$Lisp
            PLACEP(x)$Lisp => "failed"
            x
        write_!(f, x) ==
            f.fileIOmode ~= "output" =>
                error "File not in write state"
            z := PRINT_-FULL(x, f.fileState)$Lisp
            TERPRI(f.fileState)$Lisp
            x

        flush f ==
            f.fileIOmode ~= "output" => error "File not in write state"
            FORCE_-OUTPUT(f.fileState)$Lisp

@

\section{domain TEXTFILE TextFile}
<<domain TEXTFILE TextFile>>=
)abbrev domain TEXTFILE TextFile
++ Author: Stephen M. Watt
++ Date Created: 1985
++ Date Last Updated: June 4, 1991
++ Basic Operations: writeLine! readLine! readLineIfCan! readIfCan! endOfFile?
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ Description:
++   This domain provides an implementation of text files.  Text is stored
++   in these files using the native character set of the computer.

TextFile: Cat == Def where
    StreamName ==> Union(FileName, "console")

    Cat == FileCategory(FileName, String) with
        writeLine_!: (%, String) -> String
          ++ writeLine!(f,s) writes the contents of the string s
          ++ and finishes the current line in the file f.
          ++ The value of s is returned.

        writeLine_!: % -> String
          ++ writeLine!(f) finishes the current line in the file f.
          ++ An empty string is returned.  The call \spad{writeLine!(f)} is
          ++ equivalent to \spad{writeLine!(f,"")}.

        readLine_!: % -> String
          ++ readLine!(f) returns a string of the contents of a line from
          ++ the file f.

        readLineIfCan_!: % -> Union(String, "failed")
          ++ readLineIfCan!(f) returns a string of the contents of a line from
          ++ file f, if possible.  If f is not readable or if it is
          ++ positioned at the end of file, then \spad{"failed"} is returned.

        readIfCan_!: % -> Union(String, "failed")
          ++ readIfCan!(f) returns a string of the contents of a line from
          ++ file f, if possible.  If f is not readable or if it is
          ++ positioned at the end of file, then \spad{"failed"} is returned.

        endOfFile?: % -> Boolean
          ++ endOfFile?(f) tests whether the file f is positioned after the
          ++ end of all text.  If the file is open for output, then
          ++ this test is always true.

    Def == File(String) add
        FileState ==> SExpression

        Rep := Record(fileName:   FileName,    _
                      fileState:  FileState,   _
                      fileIOmode: String)

        read_! f      == readLine_! f
        readIfCan_! f == readLineIfCan_! f

        readLine_! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            s: String := read_-line(f.fileState)$Lisp
            PLACEP(s)$Lisp => error "End of file"
            s
        readLineIfCan_! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            s: String := read_-line(f.fileState)$Lisp
            PLACEP(s)$Lisp => "failed"
            s
        write_!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            PRINTEXP(x, f.fileState)$Lisp
            x
        writeLine_! f ==
            f.fileIOmode ~= "output" => error "File not in write state"
            TERPRI(f.fileState)$Lisp
            ""
        writeLine_!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            PRINTEXP(x, f.fileState)$Lisp
            TERPRI(f.fileState)$Lisp
            x
        endOfFile? f ==
          f.fileIOmode = "output" => false
          (EOFP(f.fileState)$Lisp pretend Boolean) => true
          false

@
\section{domain BINFILE BinaryFile}
<<domain BINFILE BinaryFile>>=
)abbrev domain BINFILE BinaryFile
++ Author: Barry M. Trager
++ Date Created: 1993
++ Date Last Updated:
++ Basic Operations: writeByte! readByte! readByteIfCan!
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ Description:
++   This domain provides an implementation of binary files. Data is
++   accessed one byte at a time as a small integer.

BinaryFile: Cat == Def where

    Cat == FileCategory(FileName, SingleInteger) with
        readIfCan_!: % -> Union(SingleInteger, "failed")
            ++ readIfCan!(f) returns a value from the file f, if possible.
            ++ If f is not open for reading, or if f is at the end of file
            ++ then \spad{"failed"} is the result.

--      "#": % -> SingleInteger
--          ++ #(f) returns the length of the file f in bytes.

        position: % -> SingleInteger
            ++ position(f) returns the current byte-position in the file f.

        position_!: (%, SingleInteger) -> SingleInteger
            ++ position!(f, i) sets the current byte-position to i.

    Def == File(SingleInteger) add
        FileState ==> SExpression

        Rep := Record(fileName:   FileName,    _
                      fileState:  FileState,   _
                      fileIOmode: String)

--      direc : Symbol := INTERN("DIRECTION","KEYWORD")$Lisp
--      input : Symbol := INTERN("INPUT","KEYWORD")$Lisp
--      output : Symbol := INTERN("OUTPUT","KEYWORD")$Lisp
--      eltype : Symbol := INTERN("ELEMENT-TYPE","KEYWORD")$Lisp
--      bytesize : SExpression := LIST(QUOTE(UNSIGNED$Lisp)$Lisp,8)$Lisp


        defstream(fn: FileName, mode: String): FileState ==
            mode = "input"  =>
              not readable? fn => error ["File is not readable", fn]
              BINARY__OPEN__INPUT(fn::String)$Lisp
--            OPEN(fn::String, direc, input, eltype, bytesize)$Lisp
            mode = "output" =>
              not writable? fn => error ["File is not writable", fn]
              BINARY__OPEN__OUTPUT(fn::String)$Lisp
--            OPEN(fn::String, direc, output, eltype, bytesize)$Lisp
            error ["IO mode must be input or output", mode]

        open(fname, mode) ==
            fstream := defstream(fname, mode)
            [fname, fstream, mode]

        reopen_!(f, mode) ==
            fname := f.fileName
            f.fileState := defstream(fname, mode)
            f.fileIOmode:= mode
            f

        close_! f ==
            f.fileIOmode = "output" =>
                 BINARY__CLOSE__OUTPUT()$Lisp
                 f
            f.fileIOmode = "input" =>
                  BINARY__CLOSE__INPUT()$Lisp
                  f
            error "file must be in read or write state"

        read! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            BINARY__SELECT__INPUT(f.fileState)$Lisp
            BINARY__READBYTE()$Lisp
--          READ_-BYTE(f.fileState)$Lisp
        readIfCan_! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            BINARY__SELECT__INPUT(f.fileState)$Lisp
            n:SingleInteger:=BINARY__READBYTE()$Lisp
            n = -1 => "failed"
            n::Union(SingleInteger,"failed")
--          READ_-BYTE(f.fileState,NIL$Lisp,
--                   "failed"::Union(SingleInteger,"failed"))$Lisp
        write_!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            x < 0 or x>255 => error "integer cannot be represented as a byte"
            BINARY__PRINBYTE(x)$Lisp
--          WRITE_-BYTE(x, f.fileState)$Lisp
            x

--      # f == FILE_-LENGTH(f.fileState)$Lisp
        position f ==
           f.fileIOmode ~= "input"  => error "file must be in read state"
           FILE_-POSITION(f.fileState)$Lisp
        position_!(f,i) ==
           f.fileIOmode ~= "input"  => error "file must be in read state"
           (FILE_-POSITION(f.fileState,i)$Lisp ; i)

@
\section{domain KAFILE KeyedAccessFile}
<<domain KAFILE KeyedAccessFile>>=
)abbrev domain KAFILE KeyedAccessFile
++ Author: Stephen M. Watt
++ Date Created: 1985
++ Date Last Updated: June 4, 1991
++ Basic Operations:
++ Related Domains:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ Examples:
++ References:
++ Description:
++  This domain allows a random access file to be viewed both as a table
++  and as a file object.


KeyedAccessFile(Entry): KAFcategory == KAFcapsule where
    Name  ==> FileName
    Key   ==> String
    Entry :   SetCategory

    KAFcategory ==
        Join(FileCategory(Name, Record(key: Key, entry: Entry)),
             TableAggregate(Key, Entry), finiteAggregate) with
                 pack_!: % -> %
                     ++ pack!(f) reorganizes the file f on disk to recover
                     ++ unused space.

    KAFcapsule == add

        CLASS     ==> 131   -- an arbitrary no. greater than 127
        FileState ==> SExpression
        IOMode    ==> String


        Cons:= Record(car: SExpression, cdr: SExpression)
        Rep := Record(fileName:    Name,     _
                      fileState:   FileState,   _
                      fileIOmode:  IOMode)

        defstream(fn: Name, mode: IOMode): FileState ==
            mode = "input"  =>
              RDEFINSTREAM(fn::String)$Lisp
            mode = "output" =>
              RDEFOUTSTREAM(fn::String)$Lisp
            error ["IO mode must be input or output", mode]

        ---- From Set ----
        f1 = f2 ==
            f1.fileName = f2.fileName
        coerce(f: %): OutputForm ==
            f.fileName::OutputForm

        ---- From FileCategory ----
        open fname ==
            open(fname, "either")
        open(fname, mode) ==
            mode = "either" =>
                exists? fname =>
                    open(fname, "input")
                reopen_!(open(fname, "output"), "input")
            [fname, defstream(fname, mode), mode]
        reopen_!(f, mode) ==
            close_! f
            if mode ~= "closed" then
                f.fileState := defstream(f.fileName, mode)
                f.fileIOmode  := mode
            f
        close_! f  ==
            if f.fileIOmode ~= "closed" then
                RSHUT(f.fileState)$Lisp
                f.fileIOmode  := "closed"
            f
        read_! f ==
            f.fileIOmode ~= "input" => error ["File not in read state",f]
            ks: List Symbol := RKEYIDS(f.fileName)$Lisp
            null ks => error ["Attempt to read empty file", f]
            ix := random(#ks)$Integer
            k: String := PNAME(ks.ix)$Lisp
            [k, SPADRREAD(k, f.fileState)$Lisp]
        write_!(f, pr) ==
            f.fileIOmode ~= "output" => error ["File not in write state",f]
            SPADRWRITE(pr.key, pr.entry, f.fileState)$Lisp
            pr
        name f ==
            f.fileName
        iomode f ==
            f.fileIOmode

        ---- From TableAggregate ----
        empty() ==
            fn := new("", "kaf", "sdata")$Name
            open fn
        keys f ==
            close_! f
            l: List SExpression := RKEYIDS(f.fileName)$Lisp
            [PNAME(n)$Lisp for n in l]
        # f ==
            # keys f
        elt(f,k) ==
            reopen_!(f, "input")
            SPADRREAD(k, f.fileState)$Lisp
        setelt(f,k,e) ==
            -- Leaves f in a safe, closed state.  For speed use "write".
            reopen_!(f, "output")
            UNWIND_-PROTECT(write_!(f, [k,e]), close_! f)$Lisp
            close_! f
            e
        search(k,f) ==
            not member?(k, keys f) => "failed"   -- can't trap RREAD error
            reopen_!(f, "input")
            (SPADRREAD(k, f.fileState)$Lisp)@Entry
        remove_!(k:String,f:%)  ==
            result := search(k,f)
            result case "failed" => result
            close_! f
            RDROPITEMS(NAMESTRING(f.fileName)$Lisp, LIST(k)$Lisp)$Lisp
            result
        pack_! f ==
            close_! f
            RPACKFILE(f.fileName)$Lisp
            f

@
\section{domain LIB Library}
<<domain LIB Library>>=
)abbrev domain LIB Library
++ Author: Stephen M. Watt
++ Date Created: 1985
++ Date Last Updated: June 4, 1991
++ Basic Operations:
++ Related Domains: KeyedAccessFile
++ Also See:
++ AMS Classifications:
++ Keywords:
++ Examples:
++ References:
++ Description:
++   This domain provides a simple way to save values in files.
Library(): TableAggregate(String, Any) with
         library:  FileName -> %
             ++ library(ln) creates a new library file.
         pack_!: % -> %
             ++ pack!(f) reorganizes the file f on disk to recover
             ++ unused space.

         elt : (%, Symbol) -> Any
             ++ elt(lib,k) or lib.k  extracts the value corresponding to the key \spad{k}
             ++ from the library \spad{lib}.

         setelt : (%, Symbol, Any) -> Any
             ++ \spad{lib.k := v} saves the value \spad{v} in the library
             ++ \spad{lib}.  It can later be extracted using the key \spad{k}.

         close_!: % -> %
          ++ close!(f) returns the library f closed to input and output.


    == KeyedAccessFile(Any) add
         Rep := KeyedAccessFile(Any)
         library f == open f
         elt(f:%,v:Symbol) == elt(f, string v)
         setelt(f:%, v:Symbol, val:Any) == setelt(f, string v, val)

@
\section{License}
<<license>>=
--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@
<<*>>=
<<license>>

<<category FILECAT FileCategory>>
<<domain FILE File>>
<<domain TEXTFILE TextFile>>
<<domain BINFILE BinaryFile>>
<<domain KAFILE KeyedAccessFile>>
<<domain LIB Library>>
@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}
