Subject: Re: PEEK-READ-FROM-STRING (was Re: PARSE-NUMBER)
From: Erik Naggum <erik@naggum.no>
Date: 1999/05/26
Newsgroups: comp.lang.lisp
Message-ID: <3136674082163215@naggum.no>

* Erik Naggum <erik@naggum.no>
| FWIW, I would like to know the object type of the object that would most
| likely be created if READ (etc) were applied to a string.  (that's "most
| likely" because I don't want error handling until I actually want the
| object, and I don't want to go ahead and read the string with all the
| attendant side effects until I'm sure I want the result.

* Christopher R. Barry <cbarry@2xtreme.net>
| Something like PEEK-READ and PEEK-READ-FROM-STRING
| 
|   (peek-read-from-string "1/0")
|   => #<DIVISION-BY-ZERO>

  no, nothing like that at all.  let's call it PRETEND-READ.  what I want
  PRETEND-READ to return is the _type_ of the object READ would return, and
  it should _not_ do error processing.  I actually want to capture the
  syntactic type of the object before it fails to get created.

| Cases like
| 
|   (ignore-errors
|     (read-from-string
|       "(arbitrary-function-call-invoking-massive-global-state-change)"))
| 
| come to mind.

  READ doesn't call EVAL voluntarily.  and #. can be killed in two ways:
  binding *READ-EVAL* to NIL, and nuking #\# #\. in the readtable.

| But if you wanted access to the function's arguments would you then
| manually parse the string for them or would you want
| PEEK-READ-FROM-STRING to have some way of doing this to?

  I think you just need to evaluate the above example expression to see
  what it actually returns.

| If the former, then this doesn't seem _much_ better than parsing a
| "(...)"  form and testing the CAR for FDEFINITION to see if it's a
| #<FUNCTION...> anyways.

  look, READ doesn't do _any_ magic with the forms it reads.  READ converts
  the string representation produced by WRITE into the internal form once
  given to WRITE.  that we write source code in like manner is no accident,
  but looking up the _values_ of symbols is not what READ does.

  it seems to me you're thinking in some over-simplified Scheme terms.

  BTW, in order to sanely capture errors when reading random input, I do
  some fairly complex shenanigans:

(defun safe-read-with-string (input)
  "Read an expression off of INPUT.  Return the parse value and the string.
If an error occurs, the error is returned in place of the value."
  (with-open-stream (string (make-string-output-stream))
    (with-open-stream (echo (make-echo-stream input string))
      (handler-case
	  (let ((*read-suppress* t)
		(*read-eval* nil))
	    (read-preserving-whitespace echo)
	    (values))
	(error (error)
	  (values error (get-output-stream-string string)))
	(:no-error ()
	  (let ((string (get-output-stream-string string)))
	    (handler-case
		(values (read-from-string string) string)
	      (error (error)
		(values error string)))))))))

  this means I can easily capture the offending string, which I would
  otherwise lose, and use it for error messages, and I can be ensured that
  what has been read has been as complete an object as possible, as opposed
  to when reading stops short in the middle of a list and it's non-trivial
  to get yourself back on track unless you cop out and CLEAR-INPUT.  ouch!

  (that this function is also used to capture the string representation of
  the object because the string representation is subject to MD5 checksums
  only adds to its utility and does not detract from the principal problem.)

#:Erik
-- 
@1999-07-22T00:37:33Z -- pi billion seconds since the turn of the century