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?

cl-porky11
  • 339
  • 1
  • 11
  • 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. – acelent Jun 30 '15 at 14:09

2 Answers2

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.
Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • 3
    Note that the standard does not allow to redefine standard functions, but many/most actual implementations have an implementation specific way to do so. – Rainer Joswig Jun 25 '15 at 13:38
  • That is not useful to change default reader for reading the main file – cl-porky11 Jun 25 '15 at 23:46
  • @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. – Joshua Taylor Jun 26 '15 at 00:37
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
m-n
  • 1,476
  • 1
  • 9
  • 9
  • 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 Jun 25 '15 at 23:36
  • @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. – m-n Jun 26 '15 at 05:43