(sagittarius reader) - reader macro library

Sagittarius provides functionalities to modify its reader like Common Lisp. It makes the reader programable. However it has some restriction to use. The following examples explain it.

Using reader macro

#!read-macro=sagittarius/regex
(import (sagittarius regex)) ;; usual import for procedures
#/regex/i                    ;; (sagittarius regex) defines #/regex/ form
                             ;; reader macro in it. it converts it
                             ;; (comple-regex "regex" CASE-INSENSITIVE)

Writing reader macro on toplevel

(import (rnrs) (sagittarius reader))
(set-macro-character #\$
 (lambda (port c) (error '$-reader "invliad close paren appeared")))
(set-macro-character #\! (lambda (port c) (read-delimited-list #\$ port)))
!define test !lambda !$ !display "hello reader macro"$$$
!test$ ;; prints "hello reader macro"

Writing reader macro in library and export it

#!compatible ;; make sure Sagittarius can read keyword
(library (reader macro test)
    ;; :export-reader-macro keyword must be in export clause
    (export :export-reader-macro)
    (import (rnrs) (sagittarius reader))

  (define-reader-macro $-reader #\$
    (lambda (port c)
      (error '$-reader "unexpected close paren appeared")))

  (define-reader-macro !-reader #\!
    (lambda (port c)
      (read-delimited-list #\$ port)))
)

#!read-macro=reader/macro/test  ;; imports reader macro
!define test !lambda !$ !display "hello reader macro"$$$
!test$    ;; prints "hello reader macro"

If you need to use reader macro in your library code, you need to define it outside of the library. The library syntax is just one huge list so Sagittarius can not execute the definition of reader macro inside during reading it.

Library (sagittarius reader)

This library provides reader macro procedures and macros.

Macro define-reader-macro char (name args ... ) body ...
Macro define-reader-macro name char proc
Macro define-reader-macro name char proc non-term?

Name must be self evaluated expression. Proc must accept 2 or 3 arguments, the first one is a port, the second one is a character which is defined as reader macro character, and the third one which is an optional argument is a read context.

define-reader-macro macro associates char and proc as a reader macro. Once it is associated and Sagittarius' reader reads it, then dispatches to the proc with 2 arguments.

If non-term? argument is given and not #f, the char is marked as non terminated character. So reader reads as one identifier even it it contains the given char in it.

The first form is a convenient form. Users can write a reader macro without explicitly writing lambda. The form is expanded to like this:

(define-reader-macro #\$ ($-reader args ...) body ...)
;; -> (define-reader-macro $-reader #\$ (lambda (args ...) body ...))

Note: the name is only for error message. It does not affect anything.

Macro define-dispatch-macro name char subchar proc
Macro define-dispatch-macro name char proc subchar non-term?

Name must be self evaluated expression. Proc must accept three arguments, the first one is a port, the second one is a character which is defined as reader macro character and the third one is a macro parameter.

define-dispatch-macro creates macro dispatch macro character _char_if there is not dispatch macro yet, and associates subchar and _proc_as a reader macro.

If non-term? argument is given and not #f, the char is marked as non terminated character. So reader reads as one identifier even it it contains the given char in it.

Note: the name is only for error message. It does not affect anything.

Function get-macro-character char

Returns 2 values if char is macro character; one is associated procedure other one is boolean if the char is terminated character or not. Otherwise returns 2 #f.

Function set-macro-character char proc :optional non-term?

Mark given char as macro character and sets the proc as its reader. If non-term? is given and not #f, the char will be marked as non terminated macro character.

Function make-dispatch-macro-character char :optional non-term?

Creates a new dispatch macro character with given char if it is not a dispatch macro character yet. If non-term? is given and not #f, the char will be marked as non terminated macro character.

Function get-dispatch-macro-character char subchar

Returns a procedure which is associated with char and _subchar_as a reader macro. If nothing is associated, it returns #f.

Function set-dispatch-macro-character char subchar proc

Sets proc as a reader of subchar under the dispatch macro character of char.

Function read-delimited-list char :optional (port (current-input-port))

Reads a list until given char appears.

Predefined reader macros

The following table explains predefined reader macros.

Macro character Terminated Explanation
#\( #t Reads a list until reader reads #\).
#\[ #t Reads a list until reader reads #\].
#\) #t Raises read error.
#\] #t Raises read error.
#\| #t Reads an escaped symbol until reader reads #\|.
#\" #t Reads a string until reader reads #\".
#\' #t Reads a symbol until reader reads delimited character.
#\; #t Discards read characters until reader reads a linefeed.
#\` #t Reads a next expression and returns (quasiquote _expr_)
#\, #t Check next character if it is @ and reads a next expression.

Returns (unquote-splicing _expr_) if next character was
@, otherwise (unquote _expr_)
#\: #f Only compatible mode. Reads a next expression and returns a keyword.
#\# #t(R6RS mode) Dispatch macro character.
Sub character Explanation
#\' Reads a next expression and returns (syntax _expr_).
#\` Reads a next expression and returns (quasisyntax _expr_)
#\, Check next character if it is @ and reads a next expression.

Returns (unsyntax-splicing _expr_) if next character was
@, otherwise (unsyntax _expr_)
#\! Reads next expression and set flags.
The details are described the below section
#\v Checks if the next 2 characters are u and 8 and reads
a bytevector.
#\u Only compatible mode. Checks if the next character is 8 and reads
a bytevector.
#\t and #\T Returns #t.
#\f and #\F Returns #f.
#\b and #\B Reads a binary number.
#\o and #\O Reads a octet number.
#\d and #\D Reads a decimal number.
#\x and #\X Reads a hex number.
#\i and #\I Reads a inexact number.
#\e and #\E Reads a exact number.
#\( Reads a next list and convert it to a vector.
#\; Reads a next expression and discards it.
#\| Discards the following characters until reader reads |#
#\\ Reads a character.
#\= Starts reading SRFI-38 style shared object.
#\# Refers SRFI-38 style shared object.

#! - Switching mode

Sagittarius has multiple reader and VM modes and users can switch these modes with #!. Following describes details of those modes;

#!r6rs: R6RS mode

Symbols are read according to R6RS specification and VM sets the no-overwrite and nounbound flag. With this mode, keywords are read as symbols; for example, :key is just a symbol and users can not use extended lambda syntax.

#!r7rs: R7RS mode

The mode for new specification of Scheme. This mode is less strict than R6RS mode described above. The reader can read keyword and VM sets the no-overwrite flag.

#!compatible: Compatible mode

This mode is least strict mode. In other words, it does not have any restrictions such as described above.

NOTE: If you import reader macro with #!read-macro= form and let reader reads above hash-bang, the read table will be reset. So following code will raise a read error;

#!read-macro=sagittarius/regex
#!r6rs
#/regular expression/ ;; <- &lexical

Replacing reader

Since 0.3.7, users can replace default reader. Following example describes how to replace reader.

#!reader=srfi/:49
define
  fac n
  if (zero? n) 1
    * n
      fac (- n 1)

(print (fac 10))

#!reader= specifies which reader will be used. For this example, it will use the one defined in (srfi :49) library. For compatibility of the other Scheme implementation, we chose not to use the library name itself but a bit converted name.

This is the list of #! flags:

#!r6rs

Switches to R6RS mode

#!r7rs

Switches to R7RS mode

#!compatible

Switches to compatible mode

#!no-overwrite

Sets no-overwrite flag that does not allow user to overwrite exported variables.

#!nocache

Sets disable cache flag on the current loading file

#!deprecated

Display warning message of deprecated library.

#!reader=name

Replace reader with library name. The name must be converted with the naming convention described below. For more details, see Naming convention

#!read-macro=name

name must be converted with the naming convention described below. For more details, see Naming convention

Naming convention

The naming convention is really easy. For example, replacing with (srfi :49), first remove all parentheses or brackets then replace spaces to /. Then you get srfi/:49.

This macro defines replaceable reader.

The forms are similar with define. However if you use the first form then expr must be lambda and it accept one argument.

The defined reader will be used on read time, so it needs to return valid expression as a return value of the reader.

NOTE: Only one reader can be defined in one library. If you define more than once the later one will be used.

NOTE: If you want to export user defined reader to other library, you need to put :export-reader keyword to the library export clause.