initial cut at HtDP languages via #lang; syntax/module-reader: split compiled-module info from reader info, reorganize and complete docs, don't export read-properties or get-info-getter for now
svn: r18759
This commit is contained in:
parent
d436f1deb3
commit
b559c9db9b
10
collects/htdp/asl/lang/reader.ss
Normal file
10
collects/htdp/asl/lang/reader.ss
Normal file
|
@ -0,0 +1,10 @@
|
|||
#lang s-exp syntax/module-reader
|
||||
lang/htdp-advanced
|
||||
#:read (wrap-reader read options)
|
||||
#:read-syntax (wrap-reader read-syntax options)
|
||||
#:info (make-info options)
|
||||
#:module-info (make-module-info options)
|
||||
|
||||
(require htdp/bsl/reader)
|
||||
(define options '(abbreviate-cons-as-list
|
||||
read-accept-quasiquote))
|
10
collects/htdp/bsl+/lang/reader.ss
Normal file
10
collects/htdp/bsl+/lang/reader.ss
Normal file
|
@ -0,0 +1,10 @@
|
|||
#lang s-exp syntax/module-reader
|
||||
lang/htdp-beginner-abbr
|
||||
#:read (wrap-reader read options)
|
||||
#:read-syntax (wrap-reader read-syntax options)
|
||||
#:info (make-info options)
|
||||
#:module-info (make-module-info options)
|
||||
|
||||
(require htdp/bsl/reader)
|
||||
(define options '(abbreviate-cons-as-list
|
||||
read-accept-quasiquote))
|
9
collects/htdp/bsl/lang/reader.ss
Normal file
9
collects/htdp/bsl/lang/reader.ss
Normal file
|
@ -0,0 +1,9 @@
|
|||
#lang s-exp syntax/module-reader
|
||||
lang/htdp-beginner
|
||||
#:read (wrap-reader read options)
|
||||
#:read-syntax (wrap-reader read-syntax options)
|
||||
#:info (make-info options)
|
||||
#:module-info (make-module-info options)
|
||||
|
||||
(require htdp/bsl/reader)
|
||||
(define options '())
|
8
collects/htdp/bsl/module-info.ss
Normal file
8
collects/htdp/bsl/module-info.ss
Normal file
|
@ -0,0 +1,8 @@
|
|||
#lang scheme/base
|
||||
(provide module-info)
|
||||
|
||||
(define ((module-info options) key default)
|
||||
(case key
|
||||
[(configure-runtime) `#(htdp/bsl/runtime configure ,options)]
|
||||
[else default]))
|
||||
|
18
collects/htdp/bsl/reader.ss
Normal file
18
collects/htdp/bsl/reader.ss
Normal file
|
@ -0,0 +1,18 @@
|
|||
#lang scheme/base
|
||||
(provide wrap-reader
|
||||
make-info
|
||||
make-module-info)
|
||||
|
||||
(define (wrap-reader read-proc options)
|
||||
(lambda args
|
||||
(parameterize ([read-decimal-as-inexact #f]
|
||||
[read-accept-dot #f]
|
||||
[read-accept-quasiquote (memq 'read-accept-quasiquote options)])
|
||||
(apply read-proc args))))
|
||||
|
||||
(define ((make-info options) key default use-default)
|
||||
(case key
|
||||
[else (use-default key default)]))
|
||||
|
||||
(define (make-module-info options)
|
||||
`#(htdp/bsl/module-info module-info ,options))
|
41
collects/htdp/bsl/runtime.ss
Normal file
41
collects/htdp/bsl/runtime.ss
Normal file
|
@ -0,0 +1,41 @@
|
|||
#lang scheme/base
|
||||
(require mzlib/pconvert
|
||||
scheme/pretty
|
||||
lang/private/set-result)
|
||||
|
||||
(provide configure)
|
||||
|
||||
(define (configure options)
|
||||
;; Set print-convert options:
|
||||
(booleans-as-true/false #t)
|
||||
(constructor-style-printing #t)
|
||||
(abbreviate-cons-as-list (memq 'abbreviate-cons-as-list options))
|
||||
(current-print-convert-hook
|
||||
(let ([ph (current-print-convert-hook)])
|
||||
(lambda (val basic sub)
|
||||
(cond
|
||||
[(equal? val set!-result) '(void)]
|
||||
[else (ph val basic sub)]))))
|
||||
(use-named/undefined-handler
|
||||
(lambda (x)
|
||||
(and (memq 'use-function-output-syntax options)
|
||||
(procedure? x)
|
||||
(object-name x))))
|
||||
(named/undefined-handler
|
||||
(lambda (x)
|
||||
(string->symbol
|
||||
(format "function:~a" (object-name x)))))
|
||||
;; Set pretty-print options:
|
||||
(pretty-print-show-inexactness #t)
|
||||
(pretty-print-exact-as-decimal #t)
|
||||
|
||||
;; Set print handlers to use print-convert and pretty-print:
|
||||
(current-print
|
||||
(lambda (v)
|
||||
(unless (void? v)
|
||||
(pretty-print (print-convert v)))))
|
||||
(global-port-print-handler
|
||||
(lambda (val port [depth 0])
|
||||
(let ([val (print-convert val)])
|
||||
(parameterize ([pretty-print-columns 'infinity])
|
||||
(pretty-print val port depth))))))
|
10
collects/htdp/isl+/lang/reader.ss
Normal file
10
collects/htdp/isl+/lang/reader.ss
Normal file
|
@ -0,0 +1,10 @@
|
|||
#lang s-exp syntax/module-reader
|
||||
lang/htdp-intermediate-lambda
|
||||
#:read (wrap-reader read options)
|
||||
#:read-syntax (wrap-reader read-syntax options)
|
||||
#:info (make-info options)
|
||||
#:module-info (make-module-info options)
|
||||
|
||||
(require htdp/bsl/reader)
|
||||
(define options '(abbreviate-cons-as-list
|
||||
read-accept-quasiquote))
|
10
collects/htdp/isl/lang/reader.ss
Normal file
10
collects/htdp/isl/lang/reader.ss
Normal file
|
@ -0,0 +1,10 @@
|
|||
#lang s-exp syntax/module-reader
|
||||
lang/htdp-intermediate
|
||||
#:read (wrap-reader read options)
|
||||
#:read-syntax (wrap-reader read-syntax options)
|
||||
#:info (make-info options)
|
||||
#:module-info (make-module-info options)
|
||||
|
||||
(require htdp/bsl/reader)
|
||||
(define options '(abbreviate-cons-as-list
|
||||
read-accept-quasiquote))
|
|
@ -6,13 +6,8 @@ scribble/base/lang
|
|||
#:read-syntax scribble:read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:wrapper1 (lambda (t) (list* 'doc 'values '() (t)))
|
||||
#:info
|
||||
(lambda (key defval default)
|
||||
(case key
|
||||
[(color-lexer)
|
||||
(dynamic-require 'syntax-color/scribble-lexer 'scribble-inside-lexer)]
|
||||
[(drscheme:toolbar-buttons)
|
||||
(dynamic-require 'scribble/tools/drscheme-buttons 'drscheme-buttons)]
|
||||
[else (default key defval)]))
|
||||
#:module-info (scribble-base-module-info)
|
||||
#:info (scribble-base-info)
|
||||
|
||||
(require (prefix-in scribble: "../../reader.ss"))
|
||||
(require (prefix-in scribble: "../../reader.ss")
|
||||
"../reader.ss")
|
||||
|
|
17
collects/scribble/base/reader.ss
Normal file
17
collects/scribble/base/reader.ss
Normal file
|
@ -0,0 +1,17 @@
|
|||
#lang scheme/base
|
||||
|
||||
(provide scribble-base-info
|
||||
scribble-base-module-info)
|
||||
|
||||
(define (scribble-base-info)
|
||||
(lambda (key defval default)
|
||||
(case key
|
||||
[(color-lexer)
|
||||
(dynamic-require 'syntax-color/scribble-lexer 'scribble-inside-lexer)]
|
||||
[(drscheme:toolbar-buttons)
|
||||
(dynamic-require 'scribble/tools/drscheme-buttons 'drscheme-buttons)]
|
||||
[else (default key defval)])))
|
||||
|
||||
(define (scribble-base-module-info)
|
||||
(lambda (modpath data)
|
||||
#f))
|
|
@ -1,19 +1,13 @@
|
|||
#lang scheme/base
|
||||
(require (prefix-in doc: scribble/doc/reader))
|
||||
(provide (rename-out [doc:read read]
|
||||
[my:read-syntax read-syntax])
|
||||
get-info)
|
||||
#lang s-exp syntax/module-reader
|
||||
|
||||
(define (my:read-syntax . args)
|
||||
(let ([s (apply doc:read-syntax args)])
|
||||
;; For now, remove the 'module-language property added by `doc:read-syntax'
|
||||
(syntax-property s 'module-language #f)))
|
||||
scribble/doclang
|
||||
|
||||
(define (get-info . args)
|
||||
(lambda (key defval)
|
||||
(case key
|
||||
[(color-lexer)
|
||||
(dynamic-require 'syntax-color/scribble-lexer 'scribble-inside-lexer)]
|
||||
[(drscheme:toolbar-buttons)
|
||||
(dynamic-require 'scribble/tools/drscheme-buttons 'drscheme-buttons)]
|
||||
[else defval])))
|
||||
#:read scribble:read-inside
|
||||
#:read-syntax scribble:read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:wrapper1 (lambda (t) (list* 'doc 'values '() (t)))
|
||||
#:module-info (scribble-base-module-info)
|
||||
#:info (scribble-base-info)
|
||||
|
||||
(require (prefix-in scribble: "../../reader.ss")
|
||||
scribble/base/reader)
|
||||
|
|
|
@ -1,10 +1 @@
|
|||
#lang s-exp syntax/module-reader
|
||||
|
||||
scribble/doclang
|
||||
|
||||
#:read scribble:read-inside
|
||||
#:read-syntax scribble:read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:wrapper1 (lambda (t) (list* 'doc 'values '() (t)))
|
||||
|
||||
(require (prefix-in scribble: "../reader.ss"))
|
||||
#lang scheme/base
|
||||
|
|
|
@ -6,10 +6,8 @@ scribble/jfp/lang
|
|||
#:read-syntax scribble:read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:wrapper1 (lambda (t) (cons 'doc (t)))
|
||||
#:info (lambda (key defval default)
|
||||
(case key
|
||||
[(color-lexer)
|
||||
(dynamic-require 'syntax-color/scribble-lexer 'scribble-inside-lexer)]
|
||||
[else (default key defval)]))
|
||||
#:module-info (scribble-base-module-info)
|
||||
#:info (scribble-base-info)
|
||||
|
||||
(require (prefix-in scribble: "../../reader.ss"))
|
||||
(require (prefix-in scribble: "../../reader.ss")
|
||||
scribble/base/reader)
|
||||
|
|
|
@ -5,5 +5,8 @@ scribble/lp/lang/lang
|
|||
#:read read-inside
|
||||
#:read-syntax read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:module-info (scribble-base-module-info)
|
||||
#:info (scribble-base-info)
|
||||
|
||||
(require scribble/reader)
|
||||
(require scribble/reader
|
||||
scribble/base/reader)
|
||||
|
|
|
@ -6,12 +6,8 @@ scribble/manual/lang
|
|||
#:read-syntax scribble:read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:wrapper1 (lambda (t) (cons 'doc (t)))
|
||||
#:info (lambda (key defval default)
|
||||
(case key
|
||||
[(color-lexer)
|
||||
(dynamic-require 'syntax-color/scribble-lexer 'scribble-inside-lexer)]
|
||||
[(drscheme:toolbar-buttons)
|
||||
(dynamic-require 'scribble/tools/drscheme-buttons 'drscheme-buttons)]
|
||||
[else (default key defval)]))
|
||||
#:module-info (scribble-base-module-info)
|
||||
#:info (scribble-base-info)
|
||||
|
||||
(require (prefix-in scribble: "../../reader.ss"))
|
||||
(require (prefix-in scribble: "../../reader.ss")
|
||||
scribble/base/reader)
|
||||
|
|
|
@ -41,9 +41,11 @@
|
|||
#f
|
||||
(list (hash-lang)
|
||||
spacer
|
||||
(as-modname-link
|
||||
',#'lang
|
||||
(to-element ',#'lang)))))
|
||||
,(if (identifier? #'lang)
|
||||
`(as-modname-link
|
||||
',#'lang
|
||||
(to-element ',#'lang))
|
||||
#'(scheme lang)))))
|
||||
#'lang)]
|
||||
[(file ...)
|
||||
(if (syntax-e #'filename)
|
||||
|
|
|
@ -6,10 +6,8 @@ scribble/sigplan/lang
|
|||
#:read-syntax scribble:read-syntax-inside
|
||||
#:whole-body-readers? #t
|
||||
#:wrapper1 (lambda (t) (cons 'doc (t)))
|
||||
#:info (lambda (key defval default)
|
||||
(case key
|
||||
[(color-lexer)
|
||||
(dynamic-require 'syntax-color/scribble-lexer 'scribble-inside-lexer)]
|
||||
[else (default key defval)]))
|
||||
#:module-info (scribble-base-module-info)
|
||||
#:info (scribble-base-info)
|
||||
|
||||
(require (prefix-in scribble: "../../reader.ss"))
|
||||
(require (prefix-in scribble: "../../reader.ss")
|
||||
scribble/base/reader)
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
[#:wrapper2 ~wrapper2 #'#f]
|
||||
[#:whole-body-readers? ~whole-body-readers? #'#f]
|
||||
[#:info ~info #'#f]
|
||||
[#:module-info ~module-get-info #'#f]
|
||||
[(when (equal? (and lang #t) (and ~lang #t))
|
||||
(err (string-append
|
||||
"must specify either a module language, or #:language"
|
||||
|
@ -57,7 +58,7 @@
|
|||
#,@body
|
||||
(#%provide (rename lang:read read)
|
||||
(rename lang:read-syntax read-syntax)
|
||||
read-properties get-info-getter get-info)
|
||||
get-info)
|
||||
(define (lang:read in modpath line col pos)
|
||||
(wrap-internal/wrapper #f #f in modpath line col pos))
|
||||
(define (lang:read-syntax src in modpath line col pos)
|
||||
|
@ -83,10 +84,13 @@
|
|||
[(ar? w2 3) (w2 in rd stx?)]
|
||||
[else (w2 in rd)])])
|
||||
(if stx?
|
||||
(syntax-property r
|
||||
'module-language
|
||||
(vector (syntax->datum modpath) 'get-info-getter props))
|
||||
r)))
|
||||
(let ([prop #,(if (syntax-e ~module-get-info)
|
||||
~module-get-info
|
||||
#'#f)])
|
||||
(if prop
|
||||
(syntax-property r 'module-language prop)
|
||||
r))
|
||||
r)))
|
||||
(define read-properties (lang->read-properties #,~lang))
|
||||
(define (get-info in modpath line col pos)
|
||||
(get-info-getter (read-properties in modpath line col pos)))
|
||||
|
@ -95,7 +99,7 @@
|
|||
(define data (cadr props))
|
||||
(define (default-info what defval)
|
||||
(case what
|
||||
[(module-language) (car props)]
|
||||
[(module-language) lang]
|
||||
;; ... more?
|
||||
[else defval]))
|
||||
(define info
|
||||
|
@ -176,8 +180,8 @@
|
|||
(let loop ([a null])
|
||||
(let ([v (read port)])
|
||||
(if (eof-object? v)
|
||||
(reverse a)
|
||||
(loop (cons v a)))))))]
|
||||
(reverse a)
|
||||
(loop (cons v a)))))))]
|
||||
[body (cond [(not wrapper) (body)]
|
||||
[(ar? wrapper 2) (wrapper body stx?)]
|
||||
[else (wrapper body)])]
|
||||
|
|
|
@ -5,168 +5,286 @@
|
|||
(only-in scribble/reader
|
||||
read-syntax-inside read-inside)))
|
||||
|
||||
@(begin
|
||||
(define-syntax-rule (define-mb name)
|
||||
(begin
|
||||
(require (for-label scheme/base))
|
||||
(define name @scheme[#%module-begin])))
|
||||
(define-mb scheme-#%module-begin))
|
||||
|
||||
@title[#:tag "module-reader"]{Module Reader}
|
||||
|
||||
@defmodule[syntax/module-reader]
|
||||
|
||||
The @schememodname[syntax/module-reader] language provides support for
|
||||
defining @hash-lang[] readers. In its simplest form, the only thing
|
||||
that is needed in the body of a @schememodname[syntax/module-reader]
|
||||
is the name of the module that will be used in the language position
|
||||
of read modules; using keywords, the resulting readers can be
|
||||
customized in a number of ways.
|
||||
The @schememodname[syntax/module-reader] library provides support for
|
||||
defining @hash-lang[] readers. It is normally used as a module
|
||||
language, though it may also be @scheme[require]d to get
|
||||
@scheme[make-meta-reader]. It provides all of the bindings of
|
||||
@scheme[scheme/base] other than @|scheme-#%module-begin|.
|
||||
|
||||
@defform*/subs[
|
||||
[(#%module-begin module-path)
|
||||
(#%module-begin module-path reader-option ... body ....)
|
||||
(#%module-begin reader-option ... body ....)]
|
||||
([reader-option (code:line #:language lang-expr)
|
||||
(code:line #:read read-expr)
|
||||
(#%module-begin module-path reader-option ... form ....)
|
||||
(#%module-begin reader-option ... form ....)]
|
||||
([reader-option (code:line #:read read-expr)
|
||||
(code:line #:read-syntax read-syntax-expr)
|
||||
(code:line #:info info-expr)
|
||||
(code:line #:whole-body-readers? whole?-expr)
|
||||
(code:line #:wrapper1 wrapper1-expr)
|
||||
(code:line #:wrapper2 wrapper2-expr)
|
||||
(code:line #:whole-body-readers? whole?-expr)])]{
|
||||
(code:line #:language lang-expr)
|
||||
(code:line #:info info-expr)
|
||||
(code:line #:module-info module-info-expr)])
|
||||
#:contracts ([read-expr (input-port? . -> . any/c)]
|
||||
[read-syntax-expr (any/c input-port? . -> . any/c)]
|
||||
[whole-expr any/c]
|
||||
[wrapper1-expr (or/c ((-> any/c) . -> . any/c)
|
||||
((-> any/c) boolean? . -> . any/c))]
|
||||
[wrapper2-expr (or/c (input-port? (input-port? . -> . any/c)
|
||||
. -> . any/c)
|
||||
(input-port? (input-port? . -> . any/c)
|
||||
boolean? . -> . any/c))]
|
||||
[info-expr (symbol? any/c (symbol? any/c . -> . any/c) . -> . any/c)]
|
||||
[module-info-expr (or/c (vector/c module-path? symbol? any/c) #f)]
|
||||
[lang-expr (or/c module-path?
|
||||
(and/c syntax? (compose module-path? syntax->datum))
|
||||
procedure?)])]{
|
||||
|
||||
Causes a module written in the @schememodname[syntax/module-reader]
|
||||
language to define and provide @schemeidfont{read} and
|
||||
@schemeidfont{read-syntax} functions, making the module an
|
||||
implementation of a reader. In particular, the exported reader
|
||||
functions read all S-expressions until an end-of-file, and package
|
||||
them into a new module in the @scheme[module-path] language.
|
||||
|
||||
That is, a module @scheme[_something]@scheme[/lang/reader] implemented
|
||||
as
|
||||
In its simplest form, the body of a module written with
|
||||
@schememodname[syntax/module-reader] contains just a module path,
|
||||
which is used in the language position of read modules. For example, a
|
||||
module @scheme[_something]@scheme[/lang/reader] implemented as
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
module-path)
|
||||
(module reader @#,schememodname[syntax/module-reader]
|
||||
module-path)
|
||||
]
|
||||
|
||||
creates a reader that converts @scheme[#,(hash-lang)_something] into
|
||||
creates a reader such that a module source
|
||||
|
||||
@schememod[
|
||||
@#,scheme[_something]
|
||||
....
|
||||
]
|
||||
|
||||
is read as
|
||||
|
||||
@schemeblock[
|
||||
(module _name-id module-path
|
||||
(#%module-begin ....))
|
||||
]
|
||||
|
||||
where @scheme[_name-id] is derived from the name of the port used by
|
||||
the reader, or @scheme[anonymous-module] if the port has no name.
|
||||
Keyword-based @scheme[reader-option]s allow further customization, as
|
||||
listed below. Additional @scheme[form]s are as in the body of
|
||||
@scheme[scheme/base] module; they can import bindings and define
|
||||
identifiers used by the @scheme[reader-option]s.
|
||||
|
||||
For example, @scheme[scheme/base/lang/reader] is implemented as
|
||||
@itemlist[
|
||||
|
||||
@item{@scheme[#:read] and @scheme[#:read-syntax] (both or neither
|
||||
must be supplied) specify alternate readers for parsing the
|
||||
module body---replacements @scheme[read] and
|
||||
@scheme[read-syntax], respectively. Normally, the replacements
|
||||
for @scheme[read] and @scheme[read-syntax] are applied
|
||||
repeatedly to the module source until @scheme[eof] is produced,
|
||||
but see also @scheme[#:whole-body-readers?].
|
||||
|
||||
For example, a language built on the @secref[#:doc '(lib
|
||||
"scribblings/honu/honu.scrbl")]{Honu} reader could be
|
||||
implemented with:
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
module-path
|
||||
#:read read-honu
|
||||
#:read-syntax read-honu-syntax)
|
||||
]
|
||||
|
||||
See also @scheme[#:wrapper1] and @scheme[#:wrapper2], which
|
||||
support simple parameterization of readers rather than
|
||||
wholesale replacement.}
|
||||
|
||||
@item{@scheme[#:whole-body-readers?] specified as true indicates that
|
||||
the @scheme[#:read] and @scheme[#:read-syntax] functions each produce a
|
||||
list of S-expressions or syntax objects for the module content,
|
||||
so that each is applied just once to the input stream.
|
||||
|
||||
If the resulting list contains a single form that starts with
|
||||
the symbol @scheme['#%module-begin] (or a syntax object whose
|
||||
datum is that symbol), then the first item is used as the
|
||||
module body; otherwise, a @scheme['#%module-begin] (symbol or
|
||||
identifier) is added to the beginning of the list to form the
|
||||
module body.}
|
||||
|
||||
@item{@scheme[#:wrapper1] specifies a function that controls the
|
||||
dynamic context in which the @scheme[read] and
|
||||
@scheme[read-syntax] functions are called. A
|
||||
@scheme[#:wrapper1]-specified function must accept a thunk, and
|
||||
it normally calls the thunk to produce a result while
|
||||
@scheme[parameterizing] the call. Optionally, a
|
||||
@scheme[#:wrapper1]-specified function can accept a boolean
|
||||
that indicates whether it is used in @scheme[read]
|
||||
(@scheme[#f]) or @scheme[read-syntax] (@scheme[#t]) mode.
|
||||
|
||||
For example, a language like @scheme[scheme/base] but with
|
||||
case-insensitive reading of symbols and identifiers can be
|
||||
implemented as
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
scheme/base
|
||||
#:wrapper1 (lambda (t)
|
||||
(parameterize ([read-case-sensitive #f])
|
||||
(t))))
|
||||
]
|
||||
|
||||
Using a @tech[#:doc refman]{readtable}, you can implement
|
||||
languages that are extensions of plain S-expressions.}
|
||||
|
||||
@item{@scheme[#:wrapper2] is like @scheme[#:wrapper1], but a
|
||||
@scheme[#:wrapper2]-specified function receives the input port
|
||||
to be read, and the function that it receives accepts an input
|
||||
port (usually, but not necessarily the same input port). A
|
||||
@scheme[#:wrapper2]-specified function can optionally accept an
|
||||
boolean that indicates whether it is used in @scheme[read]
|
||||
(@scheme[#f]) or @scheme[read-syntax] (@scheme[#t]) mode.}
|
||||
|
||||
@item{@scheme[#:info] specifies an implementation of reflective
|
||||
information that is used by external tools to manipulate the
|
||||
@emph{source} of modules in the language @scheme[_something]. For
|
||||
example, DrScheme uses information from @scheme[#:info] to
|
||||
determine the style of syntax coloring that it should use for
|
||||
editing a module's source.
|
||||
|
||||
The @scheme[#:info] specification should be a function of three
|
||||
arguments: a symbol indicating the kind of information
|
||||
requested (as defined by external tools), a default value that
|
||||
normally should be returned if the symbol is not recognized,
|
||||
and a default-filtering function that takes the first two
|
||||
arguments and returns a result.
|
||||
|
||||
The expression after @scheme[#:info] is placed into a context
|
||||
where @scheme[language-module] and @scheme[language-data] are
|
||||
bound. The @scheme[language-module] identifier is bound to the
|
||||
@scheme[module-path] that is used for the read module's
|
||||
language as written directly or as determined through
|
||||
@scheme[#:language]. The @scheme[language-data] identifier is
|
||||
bound to the second result from @scheme[#:language], or
|
||||
@scheme[#f] by default.
|
||||
|
||||
The default-filtering function passed to the @scheme[#:info]
|
||||
function is intended to provide support for information that
|
||||
@schememodname[syntax/module-reader] can provide automatically.
|
||||
Currently, it recognizes only the @scheme['module-language]
|
||||
key, for which it returns @scheme[language-module]; it returns
|
||||
the given default value for any other key.
|
||||
|
||||
In the case of the DrScheme syntax-coloring example, DrScheme
|
||||
supplies @scheme['color-lexer] as the symbol argument, and it
|
||||
supplies @scheme[#f] as the default. The default-filtering
|
||||
argument (i.e., the third argument to the @scheme[#:info]
|
||||
function) currently just returns the default for
|
||||
@scheme['color-lexer].}
|
||||
|
||||
@item{@scheme[#:module-info] specifies an implementation of
|
||||
reflective information that is used by external tools to
|
||||
manipulate the @emph{compiled} form of modules in the language
|
||||
@scheme[_something]. For example, when MzScheme starts a
|
||||
program, it uses information attached to the compiled main
|
||||
module to initialize the run-time environment.
|
||||
|
||||
Since the compiled form exists at a different time than when
|
||||
the source is read, a @scheme[#:module-info] specification is a
|
||||
vector that indicates an implementation of the reflective
|
||||
information, instead of a direct implementation as a function
|
||||
like @scheme[#:info]. The first element of the vector is a
|
||||
module path, the second is a symbol corresponding to a function
|
||||
exported from the module, and the last element is a value to be
|
||||
passed to the function. The last value in the vector must be
|
||||
one that can be written with @scheme[write] and read back with
|
||||
@scheme[read]. When the exported function indicated by the
|
||||
first two vector elements is called with the value from the
|
||||
last vector element, the result should be a function or two
|
||||
arguments: a symbol and a default value. The symbol and default
|
||||
value are used as for the @scheme[#:info] function (but without
|
||||
an extra default-filtering function).
|
||||
|
||||
The value specified by @scheme[#:module-info] is attached to
|
||||
the @scheme[module] form that is parsed from source through the
|
||||
@scheme['module-language] syntax property. See @scheme[module]
|
||||
for more information.
|
||||
|
||||
The expression after @scheme[#:module-info] is placed into a
|
||||
context where @scheme[language-module] are
|
||||
@scheme[language-data] are bound, the same as for
|
||||
@scheme[#:info].
|
||||
|
||||
In the case of the MzScheme run-time configuration example,
|
||||
MzScheme uses the @scheme[#:module-info] vector to obtain a
|
||||
function, and then it passes @scheme['configure-runtime] to the
|
||||
function to obtain information about configuring the runtime
|
||||
environment. See also @secref[#:doc refman "configure-runtime"].}
|
||||
|
||||
@item{@scheme[#:language] allows the language of the read
|
||||
@scheme[module] to be computed dynamically and based on the
|
||||
program source, instead of using a constant
|
||||
@scheme[module-path]. (Either @scheme[#:language] or
|
||||
@scheme[module-path] must be provided, but not both.)
|
||||
|
||||
This value of the @scheme[#:language] option can be either a
|
||||
module path (possibly as a syntax object) that is used as a
|
||||
module language, or it can be a procedure. If it is a procedure
|
||||
it can accept either
|
||||
|
||||
@itemlist[
|
||||
@item{0 arguments;}
|
||||
@item{1 argument: an input port; or}
|
||||
@item{5 arguments: an input port, a syntax object whose datum
|
||||
is a module path for the enclosing module as it was
|
||||
referenced through @hash-lang[] or
|
||||
@schememetafont{#reader}, a starting line number
|
||||
(positive exact integer) or @scheme[#f], a column
|
||||
number (non-negative exact integer) or @scheme[#f], and
|
||||
a position number (positive exact integer) or
|
||||
@scheme[#f].}
|
||||
]
|
||||
|
||||
The result can be either
|
||||
|
||||
@itemlist[
|
||||
@item{a single value, which is a module path or a syntax
|
||||
object whose datum is a module path, to be used
|
||||
like @scheme[module-path]; or}
|
||||
@item{two values, where the first is like a single-value
|
||||
result and the second can be any value.}
|
||||
]
|
||||
|
||||
The second result, which defaults to @scheme[#f] if only a
|
||||
single result is produced, is made available to the
|
||||
@scheme[#:info] and @scheme[#:module-info] functions through
|
||||
the @scheme[language-data] binding. For example, it can be a
|
||||
specification derived from the input stream that changes the
|
||||
module's reflective information (such as the syntax-coloring
|
||||
mode or the output-printing styles).}
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
scheme/base)
|
||||
]
|
||||
|
||||
The reader functions can be customized in a number of ways, using
|
||||
keyword markers in the syntax of the reader module. A @scheme[#:read]
|
||||
and @scheme[#:read-syntax] keywords can be used to specify functions
|
||||
other than @scheme[read] and @scheme[read-syntax] to perform the
|
||||
reading. For example, you can implement a
|
||||
@secref[#:doc '(lib "scribblings/honu/honu.scrbl")]{Honu} reader
|
||||
using:
|
||||
As another example, the following reader defines a ``language'' that
|
||||
ignores the contents of the file, and simply reads files as if they
|
||||
were empty:
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
honu
|
||||
#:read read-honu
|
||||
#:read-syntax read-honu-syntax)
|
||||
]
|
||||
|
||||
Similarly, the @scheme[#:info] keyword supplies a procedure to be used
|
||||
by a @scheme[get-info] export (see @scheme[read-language]). The
|
||||
procedure produced by @scheme[info-expr] should consume three
|
||||
arguments: a key value, a default result, and a default info-getting
|
||||
procedure (to be called with the key and default result for default
|
||||
handling). If @scheme[#:info] is not supplied, the default
|
||||
info-getting procedure is used.
|
||||
|
||||
You can also use the (optional) module @scheme[body] forms to provide
|
||||
more definitions that might be needed to implement your reader
|
||||
functions. For example, here is a case-insensitive reader for the
|
||||
@scheme[scheme/base] language:
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
scheme/base
|
||||
#:read (wrap read) #:read-syntax (wrap read-syntax)
|
||||
(define ((wrap reader) . args)
|
||||
(parameterize ([read-case-sensitive #f]) (apply reader args))))
|
||||
]
|
||||
|
||||
In many cases, however, the standard @scheme[read] and
|
||||
@scheme[read-syntax] are fine, as long as you can customize the
|
||||
dynamic context they're invoked at. For this, @scheme[#:wrapper1] can
|
||||
specify a function that can control the dynamic context in which the
|
||||
reader functions are called. It should evaluate to a function that
|
||||
consumes a thunk and invokes it in the right context. Here is an
|
||||
alternative definition of the case-insensitive language using
|
||||
@scheme[#:wrapper1]:
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
scheme/base
|
||||
#:wrapper1 (lambda (t)
|
||||
(parameterize ([read-case-sensitive #f])
|
||||
(t))))
|
||||
]
|
||||
|
||||
Note that using a @tech[#:doc refman]{readtable}, you can implement
|
||||
languages that are extensions of plain S-expressions.
|
||||
|
||||
In addition to this wrapper, there is also @scheme[#:wrapper2] that
|
||||
has more control over the resulting reader functions. If specified,
|
||||
this wrapper is handed the input port and a (one-argumet) reader
|
||||
function that expects the input port as an argument. This allows this
|
||||
wrapper to hand a different port value to the reader function, for
|
||||
example, it can divert the read to use different file (if given a port
|
||||
that corresponds to a file). Here is the case-insensitive implemented
|
||||
using this option:
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
scheme/base
|
||||
#:wrapper2 (lambda (in r)
|
||||
(parameterize ([read-case-sensitive #f])
|
||||
(r in))))
|
||||
]
|
||||
|
||||
In some cases, the reader functions read the whole file, so there is
|
||||
no need to iterate them (e.g., Scribble's @scheme[read-inside] and
|
||||
@scheme[read-syntax-inside]). In these cases you can specify
|
||||
@scheme[#:whole-body-readers?] as @scheme[#t] --- the readers are
|
||||
expected to return a list of expressions in this case.
|
||||
|
||||
In addition, the two wrappers can return a different value than the
|
||||
wrapped function. This introduces two more customization points for
|
||||
the resulting readers:
|
||||
@itemize[
|
||||
@item{The thunk that is passed to a @scheme[#:wrapper1] function
|
||||
reads the file contents and returns a list of read expressions
|
||||
(either syntax values or S-expressions). For example, the
|
||||
following reader defines a ``language'' that ignores the contents
|
||||
of the file, and simply reads files as if they were empty:
|
||||
@schemeblock[
|
||||
(module ignored syntax/module-reader
|
||||
scheme/base
|
||||
#:wrapper1 (lambda (t) (t) '()))]
|
||||
Note that it is still performing the read, otherwise the module
|
||||
loader will complain about extra expressions.}
|
||||
@item{The reader function that is passed to a @scheme[#:wrapper2]
|
||||
function returns the final reault of the reader (a module
|
||||
expression). You can return a different value, for example,
|
||||
making it use a different language module.}]
|
||||
In some rare cases, it is more convenient to know whether a reader is
|
||||
invoked for a @scheme[read] or for a @scheme[read-syntax]. To
|
||||
accommodate these cases, both wrappers can accept an additional
|
||||
argument, and in this case, they will be handed a boolean value that
|
||||
indicates whether the reader is expected to read syntax (@scheme[#t])
|
||||
or not (@scheme[#f]). For example, here is a reader that uses the
|
||||
scribble syntax, and the first datum in the file determines the actual
|
||||
language (which means that the library specification is effectively
|
||||
ignored):
|
||||
|
||||
Note that the wrapper still performs the read, otherwise the module
|
||||
loader would complain about extra expressions.
|
||||
|
||||
As a more useful example, the following module language is similar to
|
||||
@schememodname[at-exp], where the first datum in the file determines
|
||||
the actual language (which means that the library specification is
|
||||
effectively ignored):
|
||||
|
||||
@schemeblock[
|
||||
(module reader syntax/module-reader
|
||||
-ignored-
|
||||
|
@ -186,7 +304,7 @@ ignored):
|
|||
(require scribble/reader))
|
||||
]
|
||||
|
||||
This ability to change the language position in the resulting module
|
||||
The ability to change the language position in the resulting module
|
||||
expression can be useful in cases such as the above, where the base
|
||||
language module is chosen based on the input. To make this more
|
||||
convenient, you can omit the @scheme[module-path] and instead specify
|
||||
|
@ -210,15 +328,9 @@ concisely:
|
|||
(require scribble/reader))
|
||||
]
|
||||
|
||||
|
||||
Note: if such whole-body reader functions return a list with a single
|
||||
expression that begins with @scheme[#%module-begin], then the
|
||||
@scheme[syntax/module-reader] language will not inappropriately add
|
||||
another. This for backwards-compatibility with older code: having a
|
||||
whole-body reader functions or wrapper functions that return a
|
||||
@scheme[#%module-begin]-wrapped body is deprectaed.
|
||||
|
||||
}
|
||||
For such cases, however, the alternative reader constructor
|
||||
@scheme[make-meta-reader] implements a might tightly controlled
|
||||
reading of the module language.}
|
||||
|
||||
|
||||
@defproc[(make-meta-reader [self-sym symbol?]
|
||||
|
|
Loading…
Reference in New Issue
Block a user