154 lines
6.0 KiB
Racket
154 lines
6.0 KiB
Racket
#lang scribble/lp2
|
|
@(require "../lib/doc.rkt")
|
|
@doc-lib-setup
|
|
|
|
@title[#:style manual-doc-style]{Implementation of structures}
|
|
|
|
@(table-of-contents)
|
|
|
|
@section{@racket[define-structure]}
|
|
|
|
The @tc[remember-all] for-syntax function below memorizes its arguments across
|
|
compilations, and adds them to the file “@code{remember.rkt}”:
|
|
|
|
@CHUNK[<remember-all>
|
|
(require "remember.rkt")
|
|
|
|
(define (check-remember-all category value)
|
|
(let ([datum-value (syntax->datum (datum->syntax #f value))])
|
|
(if (not (member (cons category datum-value) all-remembered-list))
|
|
(let ((file-name (build-path (this-expression-source-directory)
|
|
"remember.rkt")))
|
|
;; Add the missing field names to all-fields.rkt
|
|
(with-output-file [port file-name] #:exists 'append
|
|
(writeln (cons category datum-value) port))
|
|
#f)
|
|
#t)))]
|
|
|
|
@CHUNK[<remember-all-errors>
|
|
(define (remember-all-errors id fallback stx)
|
|
;<remember-all-hard-error>
|
|
#`(#,id #,(for/list ([cause `(,stx ,fallback)])
|
|
(syntax/loc cause delayed-error-please-recompile))))
|
|
|
|
(define remember-err-id-list '())
|
|
|
|
(define (remember-all-errors2 placeholder cause-stx)
|
|
(let ((file-name (build-path (this-expression-source-directory)
|
|
"remember.rkt")))
|
|
(set! remember-err-id-list
|
|
(cons cause-stx remember-err-id-list))
|
|
(syntax-local-lift-module-end-declaration
|
|
#`(remember-all-hard-error #,file-name))
|
|
placeholder))]
|
|
|
|
@CHUNK[<remember-all-hard-error>
|
|
(define-syntax (remember-all-hard-error stx)
|
|
(syntax-case stx ()
|
|
([whole-stx file-name]
|
|
(raise-syntax-error
|
|
'remember-all
|
|
(format (~a "I added the identifiers ~a to my remembered list in"
|
|
" ~a . Please recompile now.")
|
|
(string-join (stx-map identifier->string
|
|
remember-err-id-list)
|
|
", ")
|
|
(syntax->datum #'file-name))
|
|
#f
|
|
#f
|
|
remember-err-id-list))))]
|
|
|
|
We can, during subsequent compilations, retrieve the list of already-memorized
|
|
fields for a given tag.
|
|
|
|
@CHUNK[<get-remembered>
|
|
(define (get-remembered category)
|
|
(cdr (or (assoc category all-remembered-alist) '(_))))]
|
|
|
|
If we start with an empty “@code{remember.rkt}” file, it will throw an error at
|
|
each call with a not-yet-remembered value. In order to avoid that, we use the
|
|
macro @tc[(delayed-error-please-recompile)], which expands to an undefined
|
|
identifier @code{please-recompile}. That error is caught later, and gives a
|
|
chance to more calls to @tc[remember-all] to be executed during macro-expansion.
|
|
We define @tc[delayed-error-please-recompile] in a submodule, to minimize the
|
|
chances that we could write a definition for that identifier.
|
|
|
|
The @tc[delayed-error-please-recompile] macro has to be
|
|
declared in a @tc[typed/racket] module so that it can be
|
|
included in @tc[typed/racket] modules too.
|
|
|
|
@itemlist[
|
|
@item{TODO: do we really need this? It is going to trigger
|
|
an error after all!}
|
|
@item{TODO: could we instead use
|
|
@racket[(syntax-local-lift-module-end)] to push a macro
|
|
throwing a @racket[raise-syntax-error] right to the end of
|
|
the module? We could this way memorize all failed
|
|
querries, and highlight them using
|
|
@racket[raise-syntax-error].}]
|
|
|
|
@CHUNK[<delayed-error-please-recompile>
|
|
(begin
|
|
(module m-please-recompile typed/racket
|
|
(define-syntax (delayed-error-please-recompile stx)
|
|
#'please-recompile)
|
|
(provide delayed-error-please-recompile))
|
|
|
|
(require (for-template 'm-please-recompile)))]
|
|
|
|
Due to a bug in scribble, the above has to be wrapped in a
|
|
@tc[begin] form, otherwises the @tc[require] statement can't
|
|
access the previously declared module.
|
|
|
|
The functions above are easier to define in a
|
|
@tc[begin-for-syntax] environment, as
|
|
@tc[remember-all-errors2] refers to the
|
|
@tc[remember-all-hard-error] macro, in the template
|
|
@tc[(syntax-local-lift-module-end-declaration
|
|
#`(remember-all-hard-error …))], and that macro
|
|
accesses mutable @tc[remember-err-id-list] and
|
|
@tc[remember-err-stx-list]. It is therefore much simpler to
|
|
define everything in the same module, in a
|
|
@tc[begin-for-syntax] block except the macro which will be
|
|
declared using @tc[define-syntax].
|
|
|
|
@chunk[<for-syntax-declarations>
|
|
(require mzlib/etc
|
|
(submod "../lib/low.rkt" untyped)
|
|
(for-syntax mzlib/etc
|
|
(submod "../lib/low.rkt" untyped)
|
|
racket/string
|
|
racket/format))
|
|
(begin-for-syntax
|
|
(provide check-remember-all
|
|
remember-all-errors
|
|
get-remembered
|
|
remember-all-errors2)
|
|
|
|
<delayed-error-please-recompile>
|
|
<remember-all>
|
|
<remember-all-errors>
|
|
<get-remembered>)
|
|
;; remember-all-hard-error is a define-syntax.
|
|
<remember-all-hard-error>]
|
|
|
|
We would however like to be able to
|
|
@racket[(require "remember-lib.lp2.rkt")] and have the
|
|
bindings it provides available at the same meta-level. We
|
|
therefore define the bindings above in a separate submodule,
|
|
@racket[require] it @tc[for-template] which has the efect of
|
|
shifting the meta-level of all the bindings one level down,
|
|
and re-provide the bindings which are now at meta-level @tc[0].
|
|
|
|
@chunk[<*>
|
|
(begin
|
|
(module for-syntax-declarations racket
|
|
<for-syntax-declarations>)
|
|
(module main racket
|
|
(require (for-template (submod ".." for-syntax-declarations)))
|
|
(provide check-remember-all
|
|
remember-all-errors
|
|
get-remembered
|
|
remember-all-errors2))
|
|
(require 'main)
|
|
(provide (all-from-out 'main)))] |