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.
This library provides reader macro procedures and macros.
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.
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.
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.
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.
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.
Returns a procedure which is associated with char and _subchar_as a reader macro. If nothing is associated, it returns #f.
Sets proc as a reader of subchar under the dispatch macro character of char.
Reads a list until given char appears.
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 readsa bytevector. |
#\u | Only compatible mode. Checks if the next character is 8 and readsa 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. |
Sagittarius has multiple reader and VM modes and users can switch these modes
with #!
. Following describes details of those modes;
#!r6rs
: R6RS modeSymbols 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 modeThe 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 modeThis 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
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
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.