diff --git a/scribble-doc/scribblings/scribble/lp-ex.rkt b/scribble-doc/scribblings/scribble/lp-ex.rkt index bfcd0abc..b23f1a03 100644 --- a/scribble-doc/scribblings/scribble/lp-ex.rkt +++ b/scribble-doc/scribblings/scribble/lp-ex.rkt @@ -1,4 +1,5 @@ -#lang scribble/lp +#lang scribble/lp2 +@(require scribble/manual) Literate programs have chunks of code, like this one: diff --git a/scribble-doc/scribblings/scribble/lp.scrbl b/scribble-doc/scribblings/scribble/lp.scrbl index 7c5bf011..1917c86c 100644 --- a/scribble-doc/scribblings/scribble/lp.scrbl +++ b/scribble-doc/scribblings/scribble/lp.scrbl @@ -2,6 +2,7 @@ @(require scribble/manual scribble/core scribble/html-properties scribble/latex-properties racket/runtime-path + racket/file "utils.rkt" (prefix-in lp-ex: "lp-ex-doc.scrbl") (for-label scribble/lp-include scribble/lp)) @@ -12,49 +13,56 @@ (make-tex-addition "lp.tex"))) ]{Literate Programming} -Programs written using @racketmodname[scribble/lp] are simultaneously -two things: a program and a document describing the program. +Programs written using @racketmodname[scribble/lp2] are simultaneously +two things: a program and a document describing the program: -Programs in @racketmodname[scribble/lp] are viewed in two different -ways, either by running the program directly or by including it with -@racket[lp-include]. When running the program, all of the -@racket[chunk] expressions are collected and stitched together into a -program, and the rest of the module is discarded. When using -@racket[lp-include], the entire contents of the module are preserved -and are treated like an ordinary Scribble document, where -@racket[chunk]s are typeset in a manner similar to @racket[codeblock]. +@itemlist[ + + @item{When the program is run, all of the @racket[chunk] expressions + are collected and stitched together into a program, and the + rest of the module is discarded.} + + @item{When the program is provided to Scribble---or used through + @racket[include-section] in another Scribble document with a + @raclet[(submod ... doc)] module path---the entire contents of + the module are treated like an ordinary Scribble document, + where @racket[chunk]s are typeset in a manner similar to + @racket[codeblock].} + +] @(define-runtime-path lp-ex "lp-ex.rkt") For example, consider this program: -@(call-with-input-file lp-ex - (lambda (port) - (verbatim - #:indent 2 - (apply - string-append - (let loop () - (let ([line (read-line port 'any)]) - (cond - [(eof-object? line) '()] - [(equal? line "") (cons " \n" (loop))] - [else - (list* line "\n" (loop))]))))))) +@(codeblock (file->string lp-ex)) When this file is @racket[require]d in the normal manner, it defines a function @racket[f] that squares its argument, and the documentation -is ignored. When it is included with @racket[lp-include], it looks -like this: +is ignored. When it is rendered as a Scribble document, the output +looks like this: @(make-nested-flow (make-style "LPBoxed" null) (part-blocks lp-ex:doc)) -@section{@racketmodname[scribble/lp] Language} +@; ------------------------------------------------------------ -@defmodulelang[scribble/lp]{The @racketmodname[scribble/lp] language -provides core support for literate programming.} +@section{@racketmodname[scribble/lp2] Language} + +@defmodulelang[scribble/lp2 #:use-sources (scribble/lp)]{The +@racketmodname[scribble/lp] language provides core support for +literate programming. It is read like a @racketmodname[scribble/base] +program, but its bindings extend @racketmodname[racket/base] with two +forms: @racket[chunk] and @racket[CHUNK].} + +More precisely, a module in @racketmodname[scribble/lp2] has its +@racketmodname[racket/base]-like content in a @racketidfont{doc} +submodule, which is recognized by tools such as @exec{raco scribble}. +The content of the @racket[chunk] and @racket[CHUNK] forms is +stitched together as the immediate content of the module. + +@history[#:added "1.8"] @defform[(chunk id form ...)]{ @@ -83,13 +91,30 @@ use @racket[UNSYNTAX]. } +@; ------------------------------------------------------------ + +@section{@racketmodname[scribble/lp] Language} + +@defmodulelang[scribble/lp]{Programs written using the older +@racketmodname[scribble/lp] language are similar to +@racketmodname[scribble/lp2] programs, except that the module cannot +be provided directly to Scribble. Instead, the document content must be +extracted using @racket[lp-include].} + +The @racketmodname[scribble/lp] language effectively binds only +@racket[chunk] and @racket[CHUNK], while all other bindings for +documentation are taken from the context where @racket[lp-include] is +used. + +@; ------------------------------------------------------------ + @section{@racketmodname[scribble/lp-include] Module} @defmodule[scribble/lp-include]{The @racketmodname[scribble/lp-include] library is normally used within a Scribble document---that is, a module that starts with something like -@racket[#, @hash-lang[] scribble/base] or @racket[#, @hash-lang[] -scribble/manual], instead of @racket[#, @hash-lang[] racket].} +@racket[#, @hash-lang[] @racketmodname[scribble/base]] or @racket[#, @hash-lang[] +@racketmodname[scribble/manual]], instead of @racket[#, @hash-lang[] @racketmodname[racket]].} @defform[(lp-include filename)]{ Includes the source of @racket[filename] as the typeset version of the literate diff --git a/scribble-lib/info.rkt b/scribble-lib/info.rkt index 27965989..1990fb6a 100644 --- a/scribble-lib/info.rkt +++ b/scribble-lib/info.rkt @@ -22,4 +22,4 @@ (define pkg-authors '(mflatt eli)) -(define version "1.7") +(define version "1.8") diff --git a/scribble-lib/scribble/lp/lang/common.rkt b/scribble-lib/scribble/lp/lang/common.rkt new file mode 100644 index 00000000..d9fe24f6 --- /dev/null +++ b/scribble-lib/scribble/lp/lang/common.rkt @@ -0,0 +1,102 @@ +#lang racket/base + +(provide (except-out (all-from-out racket/base) #%module-begin) + module-begin/plain + module-begin/doc) + +(require (for-syntax racket/base syntax/boundmap racket/list + syntax/strip-context)) + +(begin-for-syntax + (define first-id #f) + (define main-id #f) + (define (mapping-get mapping id) + (free-identifier-mapping-get mapping id (lambda () '()))) + ;; maps a chunk identifier to its collected expressions + (define chunks (make-free-identifier-mapping)) + ;; maps a chunk identifier to all identifiers that are used to define it + (define chunk-groups (make-free-identifier-mapping)) + (define (get-chunk id) (mapping-get chunks id)) + (define (add-to-chunk! id exprs) + (unless first-id (set! first-id id)) + (when (eq? (syntax-e id) '<*>) (set! main-id id)) + (free-identifier-mapping-put! + chunk-groups id + (cons id (mapping-get chunk-groups id))) + (free-identifier-mapping-put! + chunks id + `(,@(mapping-get chunks id) ,@exprs)))) + +(define-syntax (tangle stx) + (define chunk-mentions '()) + (define stupid-internal-definition-sytnax + (unless first-id + (raise-syntax-error 'scribble/lp "no chunks"))) + (define orig-stx (syntax-case stx () [(_ orig) #'orig])) + (define (restore nstx d) (datum->syntax orig-stx d nstx nstx)) + (define (shift nstx) (replace-context orig-stx nstx)) + (define body + (let ([main-id (or main-id first-id)]) + (restore + main-id + (let loop ([block (get-chunk main-id)]) + (append-map + (lambda (expr) + (if (identifier? expr) + (let ([subs (get-chunk expr)]) + (if (pair? subs) + (begin (set! chunk-mentions (cons expr chunk-mentions)) + (loop subs)) + (list (shift expr)))) + (let ([subs (syntax->list expr)]) + (if subs + (list (restore expr (loop subs))) + (list (shift expr)))))) + block))))) + (with-syntax ([(body ...) body] + ;; construct arrows manually + [((b-use b-id) ...) + (append-map (lambda (m) + (map (lambda (u) + (list (syntax-local-introduce m) + (syntax-local-introduce u))) + (mapping-get chunk-groups m))) + chunk-mentions)]) + #`(begin body ... (let ([b-id (void)]) b-use) ...))) + +(define-for-syntax (extract-chunks exprs) + (let loop ([exprs exprs]) + (syntax-case exprs () + [() (void)] + [(expr . exprs) + (syntax-case #'expr (define-syntax quote-syntax) + [(define-values (lifted) (quote-syntax (a-chunk id body ...))) + (eq? (syntax-e #'a-chunk) 'a-chunk) + (begin + (add-to-chunk! #'id (syntax->list #'(body ...))) + (loop #'exprs))] + [_ + (loop #'exprs)])]))) + +(define-for-syntax ((make-module-begin submod?) stx) + (syntax-case stx () + [(_ body0 . body) + (let ([expanded + (expand `(,#'module scribble-lp-tmp-name scribble/private/lp + ,@(strip-context #'(body0 . body))))]) + (syntax-case expanded () + [(module name lang (mb . stuff)) + (begin (extract-chunks #'stuff) + #`(#%module-begin + (tangle body0) + ;; The `doc` submodule allows a `scribble/lp` module + ;; to be provided to `scribble`: + #,@(if submod? + #`((module doc scribble/doclang2 + (require scribble/manual + (only-in scribble/private/lp chunk CHUNK)) + #,(strip-context #'(begin body0 . body)))) + '())))]))])) + +(define-syntax module-begin/plain (make-module-begin #f)) +(define-syntax module-begin/doc (make-module-begin #t)) diff --git a/scribble-lib/scribble/lp/lang/lang.rkt b/scribble-lib/scribble/lp/lang/lang.rkt index 6e1e69e8..e2fcc4d2 100644 --- a/scribble-lib/scribble/lp/lang/lang.rkt +++ b/scribble-lib/scribble/lp/lang/lang.rkt @@ -1,89 +1,7 @@ #lang racket/base +(require "common.rkt") -(provide (except-out (all-from-out racket/base) #%module-begin) - (rename-out [module-begin #%module-begin])) - -(require (for-syntax racket/base syntax/boundmap racket/list - syntax/strip-context)) - -(begin-for-syntax - (define first-id #f) - (define main-id #f) - (define (mapping-get mapping id) - (free-identifier-mapping-get mapping id (lambda () '()))) - ;; maps a chunk identifier to its collected expressions - (define chunks (make-free-identifier-mapping)) - ;; maps a chunk identifier to all identifiers that are used to define it - (define chunk-groups (make-free-identifier-mapping)) - (define (get-chunk id) (mapping-get chunks id)) - (define (add-to-chunk! id exprs) - (unless first-id (set! first-id id)) - (when (eq? (syntax-e id) '<*>) (set! main-id id)) - (free-identifier-mapping-put! - chunk-groups id - (cons id (mapping-get chunk-groups id))) - (free-identifier-mapping-put! - chunks id - `(,@(mapping-get chunks id) ,@exprs)))) - -(define-syntax (tangle stx) - (define chunk-mentions '()) - (define stupid-internal-definition-sytnax - (unless first-id - (raise-syntax-error 'scribble/lp "no chunks"))) - (define orig-stx (syntax-case stx () [(_ orig) #'orig])) - (define (restore nstx d) (datum->syntax orig-stx d nstx nstx)) - (define (shift nstx) (replace-context orig-stx nstx)) - (define body - (let ([main-id (or main-id first-id)]) - (restore - main-id - (let loop ([block (get-chunk main-id)]) - (append-map - (lambda (expr) - (if (identifier? expr) - (let ([subs (get-chunk expr)]) - (if (pair? subs) - (begin (set! chunk-mentions (cons expr chunk-mentions)) - (loop subs)) - (list (shift expr)))) - (let ([subs (syntax->list expr)]) - (if subs - (list (restore expr (loop subs))) - (list (shift expr)))))) - block))))) - (with-syntax ([(body ...) body] - ;; construct arrows manually - [((b-use b-id) ...) - (append-map (lambda (m) - (map (lambda (u) - (list (syntax-local-introduce m) - (syntax-local-introduce u))) - (mapping-get chunk-groups m))) - chunk-mentions)]) - #`(begin body ... (let ([b-id (void)]) b-use) ...))) - -(define-for-syntax (extract-chunks exprs) - (let loop ([exprs exprs]) - (syntax-case exprs () - [() (void)] - [(expr . exprs) - (syntax-case #'expr (define-syntax quote-syntax) - [(define-values (lifted) (quote-syntax (a-chunk id body ...))) - (eq? (syntax-e #'a-chunk) 'a-chunk) - (begin - (add-to-chunk! #'id (syntax->list #'(body ...))) - (loop #'exprs))] - [_ - (loop #'exprs)])]))) - -(define-syntax (module-begin stx) - (syntax-case stx () - [(_ id exprs . body) - (let ([expanded - (expand `(,#'module scribble-lp-tmp-name scribble/private/lp - ,@(strip-context #'(id exprs . body))))]) - (syntax-case expanded () - [(module name lang (mb . stuff)) - (begin (extract-chunks #'stuff) - #'(#%module-begin (tangle id)))]))])) +(provide (except-out (all-from-out "common.rkt") + module-begin/plain + module-begin/doc) + (rename-out [module-begin/plain #%module-begin])) diff --git a/scribble-lib/scribble/lp/lang/lang2.rkt b/scribble-lib/scribble/lp/lang/lang2.rkt new file mode 100644 index 00000000..299e32a2 --- /dev/null +++ b/scribble-lib/scribble/lp/lang/lang2.rkt @@ -0,0 +1,7 @@ +#lang racket/base +(require "common.rkt") + +(provide (except-out (all-from-out "common.rkt") + module-begin/plain + module-begin/doc) + (rename-out [module-begin/doc #%module-begin])) diff --git a/scribble-lib/scribble/lp/lang/reader.rkt b/scribble-lib/scribble/lp/lang/reader.rkt index bc2e2f8b..baec8293 100644 --- a/scribble-lib/scribble/lp/lang/reader.rkt +++ b/scribble-lib/scribble/lp/lang/reader.rkt @@ -5,13 +5,11 @@ scribble/lp/lang/lang #:read read-inside #:read-syntax read-syntax-inside #:whole-body-readers? #t -;; don't use scribble-base-info for the #:info arg, since -;; scribble/lp files are not directly scribble'able. #:language-info (scribble-base-language-info) -#:info (scribble-base-reader-info) +#:info (scribble-base-info) (require scribble/reader (only-in scribble/base/reader - scribble-base-reader-info + scribble-base-info scribble-base-language-info)) diff --git a/scribble-lib/scribble/lp2.rkt b/scribble-lib/scribble/lp2.rkt new file mode 100644 index 00000000..66765117 --- /dev/null +++ b/scribble-lib/scribble/lp2.rkt @@ -0,0 +1,16 @@ +#lang racket/base +(require scribble/private/lp) +(provide chunk CHUNK) + +(module reader syntax/module-reader + scribble/lp/lang/lang2 + + #:read read-inside + #:read-syntax read-syntax-inside + #:whole-body-readers? #t + #:language-info (scribble-base-language-info) + #:info (scribble-base-info) + (require scribble/reader + (only-in scribble/base/reader + scribble-base-info + scribble-base-language-info)))