[plt-scheme] read-delimited-list

From: Eli Barzilay (eli at barzilay.org)
Date: Sat Aug 19 19:24:34 EDT 2006

On Aug 19, Matthew Flatt wrote:
> At 18 Aug 2006 23:09:54 -0000, Paul Graham wrote:
> > Is there some equivalent of cl read-delimited-list in mzscheme?
> > 
> > I'm trying to make [foo _ bar] read as (lambda (_) (foo _ bar)).
> 
> You can get something like `read-delimited-list' by using
> `read/recursive' using #f to indicate the default readtable:
> 
>  (define (read-square-brackets ch port src line col pos)
>    `(lambda () 
>       ;; Recursive read starts with default readtable's [ parser,
>       ;; but nested reads still use the curent readtable:
>       ,(read/recursive port #\[ #f)))
> 
>  (parameterize ([current-readtable
>                  (make-readtable #f 
>                                  #\[ 'terminating-macro read-square-brackets)])
>    (read (open-input-string "[foo _ bar]")))

There is a missing part there -- the injected `lambda' doesn't have
the `_' binding...  This should work as expected:

  (define (read-square-brackets ch port src line col pos)
    `(lambda (_)
       ;; Recursive read starts with default readtable's [ parser,
       ;; but nested reads still use the curent readtable:
       ,(read/recursive port #\[ #f)))

but I think that there is not enough examples around for playing with
readtables, so I'll give some more details.  (So this is going to be a
little verbose.)

There are two ways to play with readtables in mzscheme -- changing the
`current-readtable' parameter or using the #reader syntax.  The first
is a little easier to play with when you're trying to play with the
new reader but the global change is not something you want (in this
case, there is a lot of PLT code that uses brackets for a completely
different purpose, so if you happen to load some library through
source code, you're likely to get some weird errors); so this method
is better for reader extensions that do not affect a lot of existing
code (like adding some new `#' reader).  The second approach avoids
the global change, so it is better for actually using it, for test
cases and for `real' code that uses the new table.  To implement the
first, all you need is some code that changes the global readtable,
and to implement the second, you need a module that provides custom
`read' and `read-syntax' functionality.

I usually like to make both ways available, by putting the code in a
module that provides `read', `read-syntax', and a `use-xxx-readtable'
for the global change.  In this case, I put this in a "r.scm" file:

  (module r mzscheme
  
    ;; The main reader function for []s
    (define (read-square-brackets ch port src line col pos)
      `(lambda (_)
         ;; Recursive read starts with default readtable's [ parser,
         ;; but nested reads still use the curent readtable:
         ,(read/recursive port #\[ #f)))
  
    ;; A readtable that is just like the builtin except for []s
    (define bracket-readtable
      (make-readtable #f #\[ 'terminating-macro read-square-brackets))
  
    ;; Call this to set the global readtable
    (provide use-bracket-readtable)
    (define (use-bracket-readtable)
      (current-readtable bracket-readtable))
  
    ;; These two implement the required functionality for #reader
    (define (*read inp)
      (parameterize ([current-readtable bracket-readtable])
        (read inp)))
    (define (*read-syntax src port)
      (parameterize ([current-readtable bracket-readtable])
        (read-syntax src port)))
    ;; ... and the need to be provided as `read' and `read-syntax'
    (provide (rename *read read) (rename *read-syntax read-syntax)))

Once that's in a file, I risk trying it using a global change:

  Welcome to MzScheme version 352, Copyright (c) 2004-2006 PLT Scheme Inc.
  > (require "r.scm")
  > (use-bracket-readtable)
  > ([+ _ 1] 10)
  11

or I can use the safer #reader to change the meaning of a single
expression:

  Welcome to MzScheme version 352, Copyright (c) 2004-2006 PLT Scheme Inc.
  > #reader"r.scm" (define (foo l) (map [* _ 2] l))
  > (foo '(1 2 3))
  (2 4 6)
  > #reader"r.scm" (define (foo l) (map [map [* _ 2] _] l))
  > (foo '((1 2 3) (4 5 6 7)))
  ((2 4 6) (8 10 12 14))

Since #reader changes the table for the following item, it can be used
for whole modules in a convenient way:

  #reader"r.scm"
  (module xx mzscheme
    (provide foo)
    (define (foo l) (map [map [* _ 2] _] l)))

There's a bunch of stuff that is useful to know about custom
readtables that I didn't mention, like how to create the syntax
objects explicitly, etc -- but in this case the default behavior is
working fine.

Also, the interface for using readtables might be extended -- it seems
that some command-line flags could be useful, but no conclusion
reached (yet).  (See PR7374)

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!


Posted on the users mailing list.