4

I wrote some function that could replace the function read of common lisp

(defun my-read (stream &rest args)
  (declare (ignore args))
  (funcall (my-get-macro-character (read-char stream))))

Is there a way to use this function as default reader?

1
  • 1
    Short answer: no, at least not portably. Things like load or compile-file might have the reader inlined, or they might refer to internal reader definitions, e.g. to bypass the optional/key argument handling overhead. Commented Jun 30, 2015 at 14:09

2 Answers 2

5

You can't redefine the built in functions1, but you can define a package that shadows cl:read and defines a new function my:read, so that when you use that package, it looks like it's the default read function. E.g., something like this:

CL-USER> (defpackage #:my-package 
           (:use "COMMON-LISP")
           (:shadow #:read)
           (:export #:read))
;=> #<PACKAGE "MY-PACKAGE">

CL-USER> (defun my-package:read (&rest args)
           (declare (ignore args))
           42)
;=> MY-PACKAGE:READ

CL-USER> (defpackage #:another-package
           (:use #:my-package "COMMON-LISP")
           (:shadowing-import-from #:my-package #:read))
;=> #<PACKAGE "ANOTHER-PACKAGE">

CL-USER> (in-package #:another-package)
;=> #<PACKAGE "ANOTHER-PACKAGE">

ANOTHER-PACKAGE> (read)
;=> 42

  1. Actually, as Rainer Joswig noted in the comments, even though it's undefined behavior (see 11.1.2.1.2 Constraints on the COMMON-LISP Package for Conforming Programs), there often are ways to redefine some of the Common Lisp function, For instance, in SBCL you can use unlock-package, as shown in redefining built-in function. CLISP has package locks. Other implementations may have similar functionality.
Sign up to request clarification or add additional context in comments.

3 Comments

Note that the standard does not allow to redefine standard functions, but many/most actual implementations have an implementation specific way to do so.
That is not useful to change default reader for reading the main file
@cl-porky11 I'm not sure exactly what you mean by "the main file". If you have a definition like this somewhere, then all you'd need to do is (in-package #:my-package) at the top of that file.
4

One approach is to use set-macro-character on all "valid" input characters in a readtable. (This is okay if you only accept ASCII input, but I don't know if it would be practical for full Unicode.)

Something like this:

(defun replace-default-read-behavior (rt fn)
  (loop for c across 
        " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
        do (set-macro-character c fn t rt)))

(defun my-parser (stream char)
  (format t "custom read: ~A~A" char (read-line stream)))

(defun my-get-macro-character (char)
  (declare (ignore char))
  #'my-parser)

(defun my-read (stream char)
  (funcall (my-get-macro-character char) stream char))

(defvar *my-readtable* (copy-readtable ()))

(replace-default-read-behavior *my-readtable* #'my-read)

(let ((*readtable* *my-readtable*))
  (read-from-string "foo"))
custom read: foo  ; printed
NIL               ; returned
3

2 Comments

I also thought about this solution, but it won't work for non-standard-characters, what might be a problem in some cases. (you forgot #\Newline in the string)
@cl-porky11 agreed. Worst case, unexpected characters could cause your program to silently misbehave. If you adjust my-read to ingest the entire input you can loosen the requirement to say just the first character of input has to be one you expect. That's still not satisfying, but I don't know of a better way to do it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.