The Revised Maclisp ManualThe PitmanualPage A-17
Published by HyperMeta Inc.
 
Prev | Index | Next
[Blue Marble]
Climate Change
Is there a more important issue?


Software File Arrays


SFAConceptSoftware File Array

There are many instances when a MacLISP programmer wishes to write a function to simulate an arbitrary I/O device. That is, he wishes to use the primitives provided in LISP for doing I/O (such as READ and PRINT) yet wishes the target/source of the characters to be completely arbitrary. Maclisp has a mechanism called an SFA (Software File Array) to handle this situation.

An SFA consists of a function and some associated local storage bound up in an SFA object. In order to satisfy the above goals, an SFA object may be used in almost all places that a file-object can be used. Note that the existence of SFA's does not obviate the need for the current New-I/O implementation of files. SFA's are strictly a user entity, and the files are still needed in order to communicate with the operating system.

The SFA handler is a user-defined function which accepts 3 arguments. Its first argument is the SFA object on which it is to act, its second argument is a symbol indicating the operation to be performed, and the third argument is any data needed for that operation. The SFA object needs to be supplied to the function because one SFA handler may be associated with many different SFA objects. There are some operations used by the system (TYI, TYO, etc...). The user is free to define new operations and to use them by calling the SFA directly.

As there are predefined uses for many of the system I/O functions that currently exist, when these functions are translated to SFA operations they must retain the semantics they have when applied to file-objects. Since this is the case, any SFA that expects to interface directly to the standard I/O routines must know about the difference between binary and character-oriented operations. File objects can do either fixnum-I/O operations (e.g., IN and OUT) or ascii-I/O operations (e.g., TYI, TYO, READ, PRINT, ...) but not both; most SFA's will probably only handle one type of I/O or the other, but they are not prevented from handling both.

The user can explicitly send an operation message to an SFA by using SFA-CALL. Usually, however, he can just call normal file operation functions and they will do the SFA-CALL implicitly. The advantage of not using explicit calls to SFA-CALL is that the code can then operate on normal files and not just SFA's.

Some sample uses for SFA's are provided at the end of this chapter.

To see a list of SFA messages sent by the Maclisp system, see the SFA Message Index.


SFAPFunction(SFAP q)

Predicate returns T if q is an SFA, or NIL otherwise.

Examples:

(SFAP 'FOO)	=>	NIL
(SFAP TYO)	=>	NIL	; Files aren't SFA's
(SFAP (SFA-CREATE #'(LAMBDA N NIL) 0. 'DEMO))
                =>	T

Creating and Using SFAs


SFA-CREATEFunction(SFA-CREATE fn i pname)

SFA-CREATE creates and returns an SFA object, which represents a handler function, given by fn, and i slots of associated local storage (see SFA-GET and SFA-STORE).

When the SFA-CREATE is done, the SFA is immediatley invoked with a WHICH-OPERATIONS call. A mask is then created corresponding to the internal functions that the SFA knows how to do. This mask is used for fast error-checking when a predefined operation is handed an SFA (e.g. TYO).

The third arg, pname, may be any lisp form. The sfa object will print something like #SFA-|pname|-nnnnn.

See also: SFA-CALL, SFA-GET, SFA-STORE


SFA-CALLFunction(SFA-CALL sfa operation data)

This is a way of manually invoking the handler for an SFA on an arbitrary operation with arbitrary data. e.g., (TYO 1 sfa) is equivalent to (SFA-CALL sfa 'TYO 1). The user of SFA-CALL is responsible for checking that operation is supported by sfa's handler; however, the operation needn't be one of the standard ones described in this section.

To see a list of SFA messages sent by the Maclisp system, see the SFA Message Index.

Local Data Storage in SFAs


SFA-GETFunction(SFA-GET sfa selector)

Reads the local storage of sfa. If the second arg, selector, is a fixnum, then it is a subscript into the user's local storage array to be accessed. This number may range from 0 to the maximum as specified when the SFA object is created (see SFA-CREATE).

If second arg, selector, is a symbol, then one of the “named” locations is accessed. Valid names are: FUNCTION, the SFA's handler function; WHICH-OPERATIONS, a list of operations of interest to the system that the SFA can perform (a subset of the SFA's WHICH-OPERATIONS list); PNAME, the object to print as the “name” of the SFA; PLIST, a general property list, for use by the user; and XCONS, a “correspondent” for a bi-directional SFA (akin to the TTYCONS concept for ttys---see documentation on the TTYCONS options to STATUS and SSTATUS).


SFA-STOREFunction(SFA-STORE sfa selector val)

Stores val in the location specified by the second arg, selector. The possible values which selector may take on are the same as those accepted by SFA-GET.

It is strongly recommended that the SFA function never be changed as the effects of such an action are expressly not guaranteed by the implementors.

General SFA Messages

This section pertains to messages which may be sent both to input and output SFAs.


WHICH-OPERATIONS  

The SFA must return a list of operations which it can perform. Every SFA must handle the WHICH-OPERATIONS message.

To see a list of SFA messages sent by the Maclisp system, see the SFA Message Index.


OPEN  

This operation is invoked if an OPEN is done with a SFA as its first argument. The SFA should return either a file-object or a SFA object. The data argument is the argument to the OPEN, or NIL if none was supplied.


CLOSE  

This operation is invoked when a CLOSE is done with a SFA as its argument. There is never a useful data arg, and the SFA should return either T if the close was successful or NIL if not (e.g. if the SFA was already "closed").


DELETEF  

No arguments will be passed to the SFA. There is no clear global semantic meaning of this.


FILEPOS  

If an argument is given to FILEPOS, it is NCONS'ed and handed to the SFA, else the SFA is given NIL. For a NIL argument, a fixnum should be returned. For one argument, the SFA should interpret the argument as a “position” to set the “file” to, and perform appropriate actions.


FILEMODE  

This message is sent when (STATUS FILEMODE sfa) is done. The SFA should return a list of adjectives describing itself. If the SFA cannot support the FILEMODE operation (as determined by a system-performed WHICH-OPERATIONS check), then Lisp will handle the request by returning a list which has the form: ((SFA) . operations), where operations are the result of a WHICH-OPERATIONS call on the SFA.


LENGTHF  

This message is sent when (LENGTHF file) is done. The return value should be a fixnum specifying the file's length.


EOFFN  

This message is sent when (EOFFN file [data]). If data is NIL, the SFA should return its eoffn. If it is not NIL, it will be a list and the SFA should set its eoffn to the car of that list.

Input-Specific SFA Messages

This section pertains to messages which are only of interest to SFAs doing input. If the SFA is to support normal LISP reading other than TYI, it must support at least TYI and UNTYI, and preferably TYIPEEK also.


TYI  

The SFA should return a fixnum representing a character. This operation may be called from a READ, READLINE, or a straight TYI (the SFA cannot depend upon being able to determine the context). The data argument is the value to return when EOF is reached (this is true for all input functions including IN).


UNTYI  

The SFA should put the argument into its input stream and on the next TYI should spew forth the saved character rather than the next one in the "stream". Note that an arbitrary number of UNTYI's may be done in a row.


TYIPEEK  

The SFA should return a fixnum (as in TYI) but should not remove the character from the input flow. Therefore, subsequent TYIPEEK's will read the same character. If the SFA cannot handle TYIPEEK, it will be simulated by a TYI/UNTYI pair of operations.


IN  

The value returned should be a fixnum that is the next binary value in the input stream. The argument is the EOF value.


READLINE  

The value returned should be a symbol that represents one "line" of the input stream. If the SFA cannot handle this operation, it is simulated in terms of TYI. The argument is the value to return upon EOF.


READ  

The value returned should be the next "object" in the input stream. If the SFA cannot handle this operation, it is simulated in terms of TYI/UNTYI. The argument is the value to return upon EOF.


LISTEN  

The total number of items (or "characters") currently held in the input buffer should be returned.


CLEAR-INPUT  

The input buffer should just be thrown away.

Output-Specific SFA Messages

This section pertains to messages which are only of interest to SFAs doing output.


TYO  

The argument is a fixnum representing a character. The value returned should be T if the SFA succeeds, or NIL if the output was unsuccessful.


OUT  

The argument is a fixnum and is output as such. The TYO/OUT pair of functions is the analog to the TYI/IN pair.


FORCE-OUTPUT  

The SFA should empty its output buffer to the logical “device” to which it is connected.


CLEAR-OUTPUT  

Any buffer is just thrown away.


PRIN1  

The SFA should print the arg in its slashified form. If the SFA cannot perform this operation, it is simulated in terms of TYO.


PRINT  

The SFA should print the arg in its slashified form preceeded by a newline and terminated by a space. If the SFA cannot perform this operation, it is simulated in terms of TYO.


PRINC  

The SFA should print the arg in its unslashified form. If the SFA cannot perform this operation, it is simulated in terms of TYO.


CURSORPOS  

The SFA will receive a list of the args given to CURSORPOS as data (with the file arg removed). If the list is empty, the current cursorpos (horizontal . vertical) should be returned. Otherwise, data is a list of the form (hpos vpos) or (sym [val]), to be interpreted as the CURSORPOS function would.


RUBOUT  

The SFA should try to remove display of the last character output or echoed to this stream.

SFAs: Sample Usage

;;; An SFA for reading from a string instead of a file.

;;; Make an abstraction for our SFAs internal storage.
(defmacro char-buffer (sfa) `(sfa-get ,sfa 0))

(defun string-stream-handler (self op data) ; a handler for our SFA
  (caseq op
    ((which-operations) '(tyi untyi tyipeek init))
    ((tyi) (pop (char-buffer self)))
    ((untyi) (push data (char-buffer self)) t)
    ((tyipeek) (car (char-buffer self)))
    ((init) (setf (char-buffer self) (exploden data)))
    (t (error "Unsupported SFA operation" `(sfa-call ,self ,op ,data)))))

(defun read-from-string (string-to-read-from) ; a user of our SFA
  (let ((string-stream (sfa-create #'string-stream-handler
                                   1. ;one slot for local storage
                                   "StringStream")))
    (sfa-call string-stream 'init string-to-read-from)
    (read string-stream)))
;; Test the function
(read-from-string "(a . (b . nil))")
=> (A B)

;; Note that as in a file, a space MUST terminate a toplevel symbol.
(read-from-string "foo ")
=> FOO

(read-from-string "foo")
;(READ-EOF #SFA-|StringStream|-70752) END OF FILE WITHIN READ

;; Note also that extra characters are ignored by our function
(read-from-string "abc def")
=> ABC
;; Functions like FLATSIZE and EXPLODE could have been defined
;; using SFAs. Here is an example of how...

;; Compiler declaration for free variable
(defvar *flatsize* 0. ;Always bound - toplevel value is actually never used
  "For communication between a flatsize sfa and its caller")

(defun flatsize-handler (self op data)		;Define a handler
  (caseq op
    ((which-operations) '(tyo))
    ((tyo) (if (not (minusp data)) ;negative values aren't chars
             (setq *flatsize* (1+ *flatsize*))))
    (t (error "Unsupported SFA operation" `(sfa-call ,self ,op ,data)))))

(defvar *flatsize-sfa* (sfa-create #'flatsize-handler 0. "Flatsize Counter")
  "Counts characters that would be typed out by incrementing *FLATSIZE*")

(defun print-width (form printer) ;a generalized FLATSIZE
  (let ((*flatsize* 0.))
    (funcall printer form *flatsize-sfa*) ;for side-effect
    *flatsize*))

;; Define our own functions which we hope are just like the
;; system-provided functions FLATSIZE and FLATC.
(defun my-flatsize (x) (print-width x #'prin1))
(defun my-flatc (x) (print-width x #'princ))
(my-flatsize "A Test")
=> 8.

(my-flatc "A Test")
=> 6.
  ;; Compiler declaration for free variable
  (defvar *exploded* nil ;Always bound - toplevel value is actually never used
    "For communication between an explosion sfa and its caller")

  (defun explosion-handler (self op data) ;Define a handler
    (caseq op
      ((which-operations) '(tyo))
      ((tyo) (cond ((not (minusp data)) (push data *exploded*))))
      (t (error "Unsupported SFA operation" `(sfa-call ,self ,op ,data)))))

  (defvar *explode-sfa* (sfa-create #'explosion-handler 0. "Janitor")
    "Piles up characters from an explosion backwards in the *EXPLODED* var.")

  (defun create-explosion (object printer) ;generalized exploding operator
    (let ((*exploded* nil))
      (funcall printer object *explode-sfa*)
      (nreverse *exploded*)))

  ;; Here we define our own functions which are like the system functions
  (defun my-exploden (x) (create-explosion x #'princ))
  (defun my-explodec (x) (mapcar #'ascii (my-exploden x)))
  (defun my-explode (x) (mapcar #'ascii (create-explosion x #'prin1)))
(my-explode '|A B|)
=> (/| A | | B /|)

(my-exploden '|A B|)
=> (65. 32. 66.)

(my-explodec '|A B|)
=> (A | | B)

[Blue Marble]
Climate Change
Why is the permafrost important?

The Revised Maclisp Manual (Sunday Morning Edition)
Published Sunday, December 16, 2007 06:17am EST, and updated Sunday, July 6, 2008.
Prev | Index | Next