diff --git a/lang/reader.rkt b/lang/reader.rkt index f3a2e8c..463e1df 100644 --- a/lang/reader.rkt +++ b/lang/reader.rkt @@ -1,24 +1,28 @@ #lang racket/base (require (only-in scribble/reader make-at-reader)) -(provide (rename-out [mb-read read] [mb-read-syntax read-syntax]) read-inner) +(provide (rename-out [pollen-read read] [pollen-read-syntax read-syntax]) read-inner) (define read-inner (make-at-reader #:command-char #\◊ #:syntax? #t #:inside? #t)) -(define (mb-read p) +(define (pollen-read p) (syntax->datum - (mb-read-syntax (object-name p) p))) + (pollen-read-syntax (object-name p) p))) (define (make-output-datum i) `(module pollen-lang-module pollen ,@i)) -(define (mb-read-syntax path-string p) - (define i (read-inner path-string p)) - (datum->syntax i - `(module pollen-lang-module pollen/main-preproc ,@i) - i)) +(define (pollen-read-syntax path-string p) + (define file-contents (read-inner path-string p)) + (define file-ext (car (regexp-match #px"\\w+$" (path->string path-string)))) + (datum->syntax file-contents + `(module pollen-lang-module ,(if (member file-ext (list "pd" "ptree")) + 'pollen/main + 'pollen/main-preproc) + ,@file-contents) + file-contents)) diff --git a/main-current.rkt b/main-current.rkt new file mode 100644 index 0000000..754af55 --- /dev/null +++ b/main-current.rkt @@ -0,0 +1,106 @@ +#lang racket/base +(require "main-imports.rkt") +(provide (except-out (all-from-out racket/base) #%module-begin) + (rename-out [module-begin #%module-begin])) + +(define-syntax-rule (module-begin expr ...) + (#%module-begin + + ;; this is here only so that dynamic-rerequire of a pollen module + ;; transitively reloads the extras also. + ;; if this isn't here, then dynamic-rerequire can't see them + ;; and thus they are not tracked for changes. + (require-extras) + + ;; We want our module language to support require & provide + ;; which are only supported at the module level, so ... + ;; create a submodule to contain the input + ;; and export as needed + + ;; doclang2_raw is a clone of scribble/doclang2 with decode disabled + ;; helpful because it collects & exports content via 'doc + (module pollen-inner pollen/lang/doclang2_raw + ;; use same requires as top of main.rkt + ;; (can't import them from surrounding module due to submodule rules) + ;; todo: how to keep these two lists in sync? + ;; and why doesn't this work: + ;; (require pollen/main-imports) + ;; (provide (all-from-out pollen/main-imports)) + (require pollen/tools pollen/main-helper pollen/top pollen/ptree sugar txexpr) + (require-and-provide-extras) ; brings in the project require files + + expr ... ; body of module + + ;; set up a hook for identifier 'here' + ;; (but under a different name to avoid macrofication) + (define inner-here-path here-path) + (provide (all-defined-out)) + (provide (all-from-out ; pollen file should bring its requires + pollen/tools))) + + (require 'pollen-inner) ; provides doc & #%top, among other things + + (define here ((bound/c path->pnode) inner-here-path)) + + ;; prepare the elements, and append inner-here-path as meta. + ;; put it first so it can be overridden by custom meta later on + (define all-elements (cons `(meta "here-path" ,inner-here-path) + (cons `(meta "here" ,here) + (cond + ;; doc is probably a list, but might be a single string + [(string? doc) (list doc)] + ;; if it's a single nx, just leave it + [(txexpr? doc) (list doc)] + ;; if it's nx content, splice it in + [(list? doc) doc])))) + + + ;; split out the metas now (in raw form) + (define-values (metas-raw main-raw) + ((bound/c split-tag-from-xexpr) 'meta (make-txexpr 'irrelevant-tag empty all-elements))) + + (define metas (make-meta-hash metas-raw)) + + ;; Policy: here in the core lang, do as little to main as possible. + ;; The point is just to set it up for further processing. + ;; Unlike Scribble, which insists on decoding, + ;; Pollen just passes through the minimally processed data. + ;; one exception: if file extension marks it as ptree, send it to the ptree decoder instead. + + ;; this tests inner-here (which is always the file name) + ;; rather than (get metas 'here) which might have been overridden. + ;; Because if it's overridden to something other than *.ptree, + ;; ptree processing will fail. + ;; This defeats rule that ptree file suffix triggers ptree decoding. + (define here-is-ptree? ((bound/c ptree-source?) ((bound/c ->path) inner-here-path))) + + (define main (apply (if here-is-ptree? + ;; ptree source files will go this way, + (bound/c ptree-source-decode) + ;; ... but other files, including pollen, will go this way. + ;; Root is treated as a function. + ;; If it's not defined elsewhere, + ;; it just hits #%top and becomes a txexpr. + root) ((bound/c get-elements) main-raw))) + + + (provide main metas here + (except-out (all-from-out 'pollen-inner) inner-here-path) ; everything from user + (rename-out (inner-here-path here-path))) ; change identifier back (now safe from macrofication) + + (module+ main + (displayln ";-------------------------") + (displayln (string-append "; pollen decoded 'main" (if here-is-ptree? " (as ptree)" ""))) + (displayln ";-------------------------") + main + (displayln "") + + (if here-is-ptree? + (displayln (format "(ptree? main) ~a" (ptree? main))) + (displayln (format "(txexpr? main) ~a" (txexpr? main)))) + (displayln "") + (displayln ";-------------------------") + (displayln "; pollen 'metas") + (displayln ";-------------------------") + metas + ))) diff --git a/main-helper.rkt b/main-helper.rkt index d174862..e846296 100644 --- a/main-helper.rkt +++ b/main-helper.rkt @@ -26,39 +26,22 @@ (define-syntax (require-project-require-files stx) (do-project-require-file-syntax stx)) - -;; here = path of this file, relative to current directory. -;; We want to make this identifier behave as a runtime function -;; This requires two steps. -;; First, define the underlying function as syntax-rule (define-syntax (get-here-path stx) (datum->syntax stx '(begin - ;; Even though begin permits defines, ;; This macro might be used in an expression context, - ;; whereupon define would cause an error. Therefore, use let. + ;; so we use let, not define. (let* ([ccr (current-contract-region)] ; trick for getting current module name - [hp (cond + [here-path (cond ;; if contract-region is called from within submodule, ;; you get a list ;; in which case, just grab the path from the front [(list? ccr) (car ccr)] ;; file isn't yet saved in drracket + ;; 'pollen-lang-module name is applied by reader [(equal? 'pollen-lang-module ccr) 'nowhere] [else ccr])]) - ;; pass complete path back as here value (as string) - ;; Why not relative to current-directory? - ;; Because current-directory can't be parameterized - ;; so raises possibility of inconsistent values. - ;; Whereas the complete path is unambiguous, - ;; and can be made relative by the caller (or otherwise altered). - ((bound/c ->string) hp))))) - - -; Second step: apply a separate syntax transform to the identifier itself -; We can't do this in one step, because if the macro goes from identifier to function definition, -; The macro processor will evaluate the body at compile-time, not at runtime. -(define-syntax here-path (λ(stx) (datum->syntax stx '(get-here-path)))) + (path->string here-path))))) diff --git a/main-preproc.rkt b/main-preproc.rkt index 29e3cc2..8c24e0e 100644 --- a/main-preproc.rkt +++ b/main-preproc.rkt @@ -1,12 +1,12 @@ #lang racket/base (provide (except-out (all-from-out racket/base) #%module-begin) - (rename-out [replacement-module-begin #%module-begin])) + (rename-out [new-module-begin #%module-begin])) -(define-syntax-rule (replacement-module-begin body ...) +(define-syntax-rule (new-module-begin body ...) (#%module-begin (module inner pollen/lang/doclang_raw - main + main-raw (λ(x) (apply string-append (cdr x))) ;; chop first linebreak with cdr () (require pollen/main-helper) @@ -16,7 +16,8 @@ body ...) (require 'inner) - (provide (all-from-out 'inner)) + (define main main-raw) + (provide (all-from-out 'inner) main) (module+ main (display main)))) diff --git a/main.rkt b/main.rkt index 754af55..15c45d8 100644 --- a/main.rkt +++ b/main.rkt @@ -1,106 +1,56 @@ #lang racket/base -(require "main-imports.rkt") + (provide (except-out (all-from-out racket/base) #%module-begin) - (rename-out [module-begin #%module-begin])) + (rename-out [new-module-begin #%module-begin])) -(define-syntax-rule (module-begin expr ...) +(define-syntax-rule (new-module-begin body-exprs ...) (#%module-begin - - ;; this is here only so that dynamic-rerequire of a pollen module - ;; transitively reloads the extras also. - ;; if this isn't here, then dynamic-rerequire can't see them - ;; and thus they are not tracked for changes. - (require-extras) - - ;; We want our module language to support require & provide - ;; which are only supported at the module level, so ... - ;; create a submodule to contain the input - ;; and export as needed - - ;; doclang2_raw is a clone of scribble/doclang2 with decode disabled - ;; helpful because it collects & exports content via 'doc - (module pollen-inner pollen/lang/doclang2_raw - ;; use same requires as top of main.rkt - ;; (can't import them from surrounding module due to submodule rules) - ;; todo: how to keep these two lists in sync? - ;; and why doesn't this work: - ;; (require pollen/main-imports) - ;; (provide (all-from-out pollen/main-imports)) - (require pollen/tools pollen/main-helper pollen/top pollen/ptree sugar txexpr) - (require-and-provide-extras) ; brings in the project require files + ;; first three lines are positional arguments + (module inner pollen/lang/doclang_raw + main-raw + (λ(x) (cdr x)) ;; chop first linebreak with cdr + () + (require pollen/main-helper pollen/top ) + (require-project-require-files) + (provide (all-defined-out)) - expr ... ; body of module + ;; Build 'here + (define here-path (get-here-path)) + (require (only-in xml xexpr->string)) + (require (only-in racket/path find-relative-path)) + (require (only-in pollen/file-tools ->output-path)) + (require (only-in pollen/world PROJECT_ROOT)) + (define (path->pnode path) + (path->string (->output-path (find-relative-path PROJECT_ROOT path)))) + (define here (path->pnode here-path)) - ;; set up a hook for identifier 'here' - ;; (but under a different name to avoid macrofication) - (define inner-here-path here-path) - (provide (all-defined-out)) - (provide (all-from-out ; pollen file should bring its requires - pollen/tools))) + body-exprs ...) - (require 'pollen-inner) ; provides doc & #%top, among other things + (require 'inner) - (define here ((bound/c path->pnode) inner-here-path)) + ;; function to split tag out of txexpr + (require txexpr) - ;; prepare the elements, and append inner-here-path as meta. - ;; put it first so it can be overridden by custom meta later on - (define all-elements (cons `(meta "here-path" ,inner-here-path) - (cons `(meta "here" ,here) - (cond - ;; doc is probably a list, but might be a single string - [(string? doc) (list doc)] - ;; if it's a single nx, just leave it - [(txexpr? doc) (list doc)] - ;; if it's nx content, splice it in - [(list? doc) doc])))) + ;; split out the metas. Might include user-defined metas. + ;; But first, append here-path and here as meta. + ;; so they can be overridden by custom meta later + ;; 'root is the hook for the decoder function. + ;; If it's not defined elsewhere, it just hits #%top and becomes a txexpr. + (define one-with-everything `(root + ,@(cons `(meta "here-path" ,here-path) + (cons `(meta "here" ,here) + main-raw)))) - ;; split out the metas now (in raw form) - (define-values (metas-raw main-raw) - ((bound/c split-tag-from-xexpr) 'meta (make-txexpr 'irrelevant-tag empty all-elements))) + (define is-meta-element? (λ(x) (and (txexpr? x) (equal? 'meta (get-tag x))))) + (define-values (metas-raw main-without-metas) + (splitf-txexpr one-with-everything is-meta-element?)) - (define metas (make-meta-hash metas-raw)) + (define meta-element-to-pair (λ(x) (cons (cadr x) (caddr x)))) + (define metas (make-hash (map meta-element-to-pair metas-raw))) + (define main main-without-metas) - ;; Policy: here in the core lang, do as little to main as possible. - ;; The point is just to set it up for further processing. - ;; Unlike Scribble, which insists on decoding, - ;; Pollen just passes through the minimally processed data. - ;; one exception: if file extension marks it as ptree, send it to the ptree decoder instead. - - ;; this tests inner-here (which is always the file name) - ;; rather than (get metas 'here) which might have been overridden. - ;; Because if it's overridden to something other than *.ptree, - ;; ptree processing will fail. - ;; This defeats rule that ptree file suffix triggers ptree decoding. - (define here-is-ptree? ((bound/c ptree-source?) ((bound/c ->path) inner-here-path))) - - (define main (apply (if here-is-ptree? - ;; ptree source files will go this way, - (bound/c ptree-source-decode) - ;; ... but other files, including pollen, will go this way. - ;; Root is treated as a function. - ;; If it's not defined elsewhere, - ;; it just hits #%top and becomes a txexpr. - root) ((bound/c get-elements) main-raw))) - - - (provide main metas here - (except-out (all-from-out 'pollen-inner) inner-here-path) ; everything from user - (rename-out (inner-here-path here-path))) ; change identifier back (now safe from macrofication) + (provide (all-from-out 'inner) metas main) (module+ main - (displayln ";-------------------------") - (displayln (string-append "; pollen decoded 'main" (if here-is-ptree? " (as ptree)" ""))) - (displayln ";-------------------------") - main - (displayln "") - - (if here-is-ptree? - (displayln (format "(ptree? main) ~a" (ptree? main))) - (displayln (format "(txexpr? main) ~a" (txexpr? main)))) - (displayln "") - (displayln ";-------------------------") - (displayln "; pollen 'metas") - (displayln ";-------------------------") - metas - ))) + (print main)))) diff --git a/render.rkt b/render.rkt index b53fdb4..c685bb0 100644 --- a/render.rkt +++ b/render.rkt @@ -164,11 +164,11 @@ (define render-needed? (or - force-render - (not (file-exists? output-path)) - (mod-date-expired? source-path) - (let ([source-reloaded? (handle-source-rerequire source-path)]) - source-reloaded?))) + force-render + (not (file-exists? output-path)) + (mod-date-expired? source-path) + (let ([source-reloaded? (handle-source-rerequire source-path)]) + source-reloaded?))) (if render-needed? (render-preproc-source source-path output-path) @@ -220,8 +220,6 @@ ;; todo: this won't work with source files nested down one level (define-values (source-dir ignored also-ignored) (split-path source-path)) - ;; find out whether source had to be reloaded - (define source-reloaded? (handle-source-rerequire source-path)) ;; Then the rest: ;; set the template, render the source file with template, and catch the output. @@ -262,7 +260,8 @@ ;; c) mod-dates indicates render is needed (mod-date-expired? source-path template-path) ;; d) dynamic-rerequire indicates the source had to be reloaded - source-reloaded?) + (let ([source-reloaded? (handle-source-rerequire source-path)]) + source-reloaded?)) (begin (message "Rendering source" (->string (file-name-from-path source-path)) "with template" (->string (file-name-from-path template-path))) @@ -306,24 +305,24 @@ ;; the eval namespace doesn't have to re-import these ;; because otherwise, most of its time is spent traversing imports. (for-each (λ(mod-name) (namespace-attach-module original-ns mod-name)) - '(web-server/templates - xml/path - racket/port - racket/file - racket/rerequire - racket/contract - racket/list - pollen/debug - pollen/decode - pollen/file-tools - ; pollen/main-imports - ; pollen/main-preproc-imports - pollen/predicates - pollen/ptree - sugar - pollen/template - pollen/tools - pollen/world)) + '(web-server/templates + xml/path + racket/port + racket/file + racket/rerequire + racket/contract + racket/list + pollen/debug + pollen/decode + pollen/file-tools + ; pollen/main-imports + ; pollen/main-preproc-imports + pollen/predicates + pollen/ptree + sugar + pollen/template + pollen/tools + pollen/world)) (namespace-require 'racket/base) ; use namespace-require for FIRST require, then eval after (eval eval-string (current-namespace)))) @@ -344,7 +343,6 @@ ;; for include-template (used below) (require web-server/templates) ;; for ptree navigation functions, and template commands - ;; todo: main-helper is here for #%top and bound/c — should they go elsewhere? (require pollen/debug pollen/ptree pollen/template pollen/top) ;; import source into eval space. This sets up main & metas (require ,(->string source-name)) diff --git a/tools.rkt b/tools.rkt index 4f22c50..619b3c0 100644 --- a/tools.rkt +++ b/tools.rkt @@ -1,49 +1,5 @@ #lang racket/base -(require racket/contract racket/list) -(require txexpr sugar "debug.rkt" "predicates.rkt" "world.rkt") -(provide (all-from-out "debug.rkt" "predicates.rkt" racket/list)) +(require racket/list "debug.rkt" "predicates.rkt") +(provide (all-from-out racket/list "debug.rkt" "predicates.rkt")) -;; setup for test cases -(module+ test (require rackunit)) - -;; convert list of meta tags to a hash for export from pollen document. -;; every meta is form (meta "key" "value") (enforced by contract) -;; later metas with the same name will override earlier ones. -(define+provide/contract (make-meta-hash mxs) - ((listof meta-xexpr?) . -> . hash?) - (apply hash (append-map get-elements mxs))) - -(module+ test - (check-equal? (make-meta-hash '((meta "foo" "bar")(meta "hee" "haw"))) - (hash "foo" "bar" "hee" "haw")) - (check-equal? (make-meta-hash '((meta "foo" "bar")(meta "foo" "haw"))) - (hash "foo" "haw"))) - - - -;; function to split tag out of txexpr -(define+provide/contract (split-tag-from-xexpr tag tx) - (txexpr-tag? txexpr? . -> . (values (listof txexpr-element?) txexpr? )) - (define matches '()) - (define (extract-tag x) - (cond - [(and (txexpr? x) (equal? tag (car x))) - ; stash matched tag but return empty value - (begin - (set! matches (cons x matches)) - empty)] - [(txexpr? x) (let-values([(tag attr body) (txexpr->values x)]) - (make-txexpr tag attr (extract-tag body)))] - [(txexpr-elements? x) (filter-not empty? (map extract-tag x))] - [else x])) - (define tx-extracted (extract-tag tx)) ;; do this first to fill matches - (values (reverse matches) tx-extracted)) - - -(module+ test - (define xx '(root (meta "foo" "bar") "hello" "world" (meta "foo2" "bar2") - (em "goodnight" "moon" (meta "foo3" "bar3")))) - (check-equal? (call-with-values (λ() (split-tag-from-xexpr 'meta xx)) list) - (list '((meta "foo" "bar") (meta "foo2" "bar2") (meta "foo3" "bar3")) - '(root "hello" "world" (em "goodnight" "moon"))))) \ No newline at end of file