diff --git a/command.rkt b/command.rkt index e95e9b0..298c81e 100644 --- a/command.rkt +++ b/command.rkt @@ -12,9 +12,9 @@ [("start") `(require "server.rkt")] [("regenerate") `(begin ;; todo: take extensions off the comand line - (displayln "Regenerate preproc & pmap files ...") + (displayln "Regenerate preproc & ptree files ...") (require "regenerate.rkt" "pollen-file-tools.rkt" "world.rkt") - (apply regenerate-with-session (append-map project-files-with-ext (list POLLEN_PREPROC_EXT POLLEN_MAP_EXT))))] + (apply regenerate-with-session (append-map project-files-with-ext (list POLLEN_PREPROC_EXT POLLEN_TREE_EXT))))] [("clone") (let ([target-path (if (> (len args) 1) (->path (get args 1)) @@ -29,7 +29,7 @@ pollen-source? preproc-source? template-source? - pmap-source? + ptree-source? pollen-script? magic-directory? racket-file?))) diff --git a/debug.rkt b/debug.rkt index dcca714..b98d901 100644 --- a/debug.rkt +++ b/debug.rkt @@ -14,8 +14,9 @@ (williams:describe x) x) - ; debug utilities +(define months (list "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")) + (define (message . items) (define (zero-fill str count) (set! str (~a str)) @@ -25,15 +26,15 @@ (define (make-date-string) (define date (current-date)) - (define date-fields (map (λ(x) (zero-fill x 2)) - (list (date-month date) + (define date-fields (map (λ(x) (zero-fill x 2)) (list (date-day date) - (date-year date) + (list-ref months (sub1 (date-month date))) (modulo (date-hour date) 12) (date-minute date) (date-second date) - (if (< (date-hour date) 12) "am" "pm")))) - (apply format "[~a.~a.~a ~a:~a:~a~a]" date-fields)) + ; (if (< (date-hour date) 12) "am" "pm") + ))) + (apply format "[~a-~a ~a:~a:~a]" date-fields)) (displayln (string-join `(,(make-date-string) ,@(map (λ(x)(if (string? x) x (~v x))) items))) (current-error-port))) diff --git a/main.rkt b/main.rkt index 4160cfd..e98a5a3 100644 --- a/main.rkt +++ b/main.rkt @@ -1,10 +1,10 @@ #lang racket/base (require racket/list) (require (planet mb/pollen/tools) (planet mb/pollen/main-helper)) -(require (only-in (planet mb/pollen/pmap-decode) pmap-source-decode)) -(require (only-in (planet mb/pollen/predicates) pmap?)) +(require (only-in (planet mb/pollen/ptree-decode) ptree-source-decode)) +(require (only-in (planet mb/pollen/predicates) ptree?)) (require (only-in (planet mb/pollen/pollen-file-tools) has-ext?)) -(require (only-in (planet mb/pollen/world) POLLEN_MAP_EXT)) +(require (only-in (planet mb/pollen/world) POLLEN_TREE_EXT)) (provide (except-out (all-from-out racket/base) #%module-begin) (rename-out [module-begin #%module-begin])) @@ -75,18 +75,18 @@ ;; 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 pmap, send it to the pmap decoder instead. + ;; 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 *.pmap, - ;; pmap processing will fail. - ;; This defeats rule that pmap file suffix triggers pmap decoding. - (define here-is-pmap? (pmap-source? (->path inner-here))) + ;; 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? (ptree-source? (->path inner-here))) - (define main (apply (if here-is-pmap? - ;; pmap source files will go this way, - pmap-source-decode + (define main (apply (if here-is-ptree? + ;; ptree source files will go this way, + ptree-source-decode ;; ... but other files, including pollen, will go this way. ;; Root is treated as a function. ;; If it's not defined elsewhere, @@ -99,13 +99,13 @@ (module+ main (displayln ";-------------------------") - (displayln (string-append "; pollen decoded 'main" (if here-is-pmap? " (as pmap)" ""))) + (displayln (string-append "; pollen decoded 'main" (if here-is-ptree? " (as ptree)" ""))) (displayln ";-------------------------") main (displayln "") - (if here-is-pmap? - (displayln (format "(pmap? main) ~a" (pmap? main))) + (if here-is-ptree? + (displayln (format "(ptree? main) ~a" (ptree? main))) (displayln (format "(tagged-xexpr? main) ~a" (tagged-xexpr? main)))) (displayln "") (displayln ";-------------------------") diff --git a/pmap.rkt b/pmap.rkt deleted file mode 100644 index 0a7a286..0000000 --- a/pmap.rkt +++ /dev/null @@ -1,236 +0,0 @@ -#lang racket/base -(require xml xml/path racket/list racket/string racket/contract racket/match racket/set) -(require "tools.rkt" "world.rkt" "pmap-decode.rkt") - -(module+ test (require rackunit)) - -(provide (all-defined-out)) - -;; function to set up the project-pmap. -;; this is to make life simpler when using map navigation functions. -;; the current main.pmap of the project is used as the default input. -;; without this, you'd have to pass it over and over. -;; which is sort of the functional lifestyle, -;; but in templates, gets tiresome and error-prone. -(define/contract (make-project-pmap) - (-> pmap?) - (define pmap-source (build-path START_DIR DEFAULT_POLLEN_MAP)) - (if (file-exists? pmap-source) - ;; Load it from default path. - ;; dynamic require of a pmap source file gets you a full pmap. - (dynamic-require pmap-source POLLEN_ROOT) - ;; ... or else synthesize it - (let* ([files (directory-list START_DIR)] - ;; restrict files to those with pollen extensions - [files (map remove-ext (filter (λ(x) (has-ext? x POLLEN_SOURCE_EXT)) files))]) - ;; make a POLLEN_MAP_ROOT_NAME structure and convert it to a full pmap - (pmap-root->pmap (cons POLLEN_MAP_ROOT_NAME (map path->string files)))))) - -(define project-pmap (make-project-pmap)) - - -;; remove parents from map (i.e., just remove attrs) -;; is not the inverse of add-parents, i.e., you do not get back your original input. -(define/contract (remove-parents mt) - (pmap? . -> . tagged-xexpr?) - (remove-attrs mt)) - -(module+ test - (check-equal? (remove-parents - `(pmap-main ((,POLLEN_MAP_PARENT_KEY "")) (foo ((,POLLEN_MAP_PARENT_KEY ""))) (bar ((,POLLEN_MAP_PARENT_KEY ""))) (one ((,POLLEN_MAP_PARENT_KEY "")) (two ((,POLLEN_MAP_PARENT_KEY "one")) (three ((,POLLEN_MAP_PARENT_KEY "two"))))))) - '(pmap-main (foo) (bar) (one (two (three)))))) - - -(module+ test - (let ([sample-main `(POLLEN_MAP_ROOT_NAME "foo" "bar" (one (two "three")))]) - (check-equal? (pmap-root->pmap sample-main) - `(POLLEN_MAP_ROOT_NAME ((,POLLEN_MAP_PARENT_KEY "")) (foo ((,POLLEN_MAP_PARENT_KEY "POLLEN_MAP_ROOT_NAME"))) (bar ((,POLLEN_MAP_PARENT_KEY "POLLEN_MAP_ROOT_NAME"))) (one ((,POLLEN_MAP_PARENT_KEY "POLLEN_MAP_ROOT_NAME")) (two ((,POLLEN_MAP_PARENT_KEY "one")) (three ((,POLLEN_MAP_PARENT_KEY "two"))))))))) - - - -;; return the parent of a given name -(define/contract (parent element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c string? boolean?)) - (and element (let ([result (se-path* `(,(->symbol element) #:parent) pmap)]) - (and result (->string result))))) ; se-path* returns #f if nothing found - - -(module+ test - (define test-pmap-main `(pmap-main "foo" "bar" (one (two "three")))) - (define test-pmap (pmap-root->pmap test-pmap-main)) - (check-equal? (parent 'three test-pmap) "two") - (check-equal? (parent "three" test-pmap) "two") - (check-false (parent 'nonexistent-name test-pmap))) - - - -; get children of a particular element -(define/contract (children element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c list? boolean?)) - ;; se-path*/list returns '() if nothing found - (and element (let ([children (se-path*/list `(,(->symbol element)) pmap)]) - ; If there are sublists, just take first element - (and (not (empty? children)) (map (λ(i) (->string (if (list? i) (car i) i))) children))))) - -(module+ test - (check-equal? (children 'one test-pmap) (list "two")) - (check-equal? (children 'two test-pmap) (list "three")) - (check-false (children 'three test-pmap)) - (check-false (children 'fooburger test-pmap))) - - -;; find all siblings on current level: go up to parent and ask for children -(define/contract (siblings element [pmap project-pmap]) - ;; this never returns false: element is always a sibling of itself. - ;; todo: how to use input value in contract? e.g., to check that element is part of output list - ((pmap-key?) (pmap?) . ->* . (or/c list? boolean?)) - (children (parent element pmap) pmap)) - -(module+ test - (check-equal? (siblings 'one test-pmap) '("foo" "bar" "one")) - (check-equal? (siblings 'foo test-pmap) '("foo" "bar" "one")) - (check-equal? (siblings 'two test-pmap) '("two")) - (check-false (siblings 'invalid-key test-pmap))) - - - -(define/contract (siblings-split element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (values (or/c (listof pmap-key?) boolean?) - (or/c (listof pmap-key?) boolean?))) - (let-values ([(left right) (splitf-at (siblings element pmap) - (λ(e) (not (equal? (->string e) (->string element)))))]) - (values (if (empty? left) #f left) (if (empty? (cdr right)) #f (cdr right))))) - -(module+ test - (check-equal? (values->list (siblings-split 'one test-pmap)) '(("foo" "bar") #f)) - (check-equal? (values->list (siblings-split 'bar test-pmap)) (list '("foo") '("one")))) - - -;; siblings to the left of target element (i.e., precede in map order) -(define (siblings-left element [pmap project-pmap]) - (let-values ([(left right) (siblings-split element pmap)]) - left)) - -(module+ test - (check-equal? (siblings-left 'one test-pmap) '("foo" "bar")) - (check-false (siblings-left 'foo test-pmap))) - -;; siblings to the right of target element (i.e., follow in map order) -(define (siblings-right element [pmap project-pmap]) - (let-values ([(left right) (siblings-split element pmap)]) - right)) - -(module+ test - (check-false (siblings-right 'one test-pmap)) - (check-equal? (siblings-right 'foo test-pmap) '("bar" "one"))) - - -;; get element immediately to the left in map -(define/contract (sibling-previous element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c string? boolean?)) - (let ([siblings (siblings-left element pmap)]) - (and siblings (last siblings)))) - -(module+ test - (check-equal? (sibling-previous 'bar test-pmap) "foo") - (check-false (sibling-previous 'foo test-pmap))) - -;; get element immediately to the right in map -(define/contract (sibling-next element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c string? boolean?)) - (let ([siblings (siblings-right element pmap)]) - (and siblings (first siblings)))) - -(module+ test - (check-equal? (sibling-next 'foo test-pmap) "bar") - (check-false (sibling-next 'one test-pmap))) - - -;; flatten map to sequence -(define/contract (all-pages [pmap project-pmap]) - (pmap? . -> . (listof string?)) - ; use cdr to get rid of main-map tag at front - (map ->string (cdr (flatten (remove-parents pmap))))) - -(module+ test - (check-equal? (all-pages test-pmap) '("foo" "bar" "one" "two" "three"))) - -;; helper function for get-previous-pages and get-next-pages -(define/contract (adjacent-pages side element [pmap project-pmap]) - ((symbol? pmap-key?) (pmap?) . ->* . (or/c list? boolean?)) - (let ([result ((if (equal? side 'left) - takef - takef-right) (all-pages pmap) - (λ(y) (not (equal? (->string element) (->string y)))))]) - (and (not (empty? result)) result))) - -(module+ test - (check-equal? (adjacent-pages 'left 'one test-pmap) '("foo" "bar")) - (check-equal? (adjacent-pages 'left 'three test-pmap) '("foo" "bar" "one" "two")) - (check-false (adjacent-pages 'left 'foo test-pmap))) - - -;; get sequence of earlier pages -(define/contract (previous-pages element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c list? boolean?)) - (adjacent-pages 'left element pmap)) - -(module+ test - (check-equal? (previous-pages 'one test-pmap) '("foo" "bar")) - (check-equal? (previous-pages 'three test-pmap) '("foo" "bar" "one" "two")) - (check-false (previous-pages 'foo test-pmap))) - - -;; get sequence of next pages -(define (next-pages element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c list? boolean?)) - (adjacent-pages 'right element pmap)) - -(module+ test - (check-equal? (next-pages 'foo test-pmap) '("bar" "one" "two" "three")) - (check-equal? (next-pages 'one test-pmap) '("two" "three")) - (check-false (next-pages 'three test-pmap))) - -;; get page immediately previous -(define/contract (previous-page element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c string? boolean?)) - (let ([result (previous-pages element pmap)]) - (and result (last result)))) - -(module+ test - (check-equal? (previous-page 'one test-pmap) "bar") - (check-equal? (previous-page 'three test-pmap) "two") - (check-false (previous-page 'foo test-pmap))) - -;; get page immediately next -(define (next-page element [pmap project-pmap]) - ((pmap-key?) (pmap?) . ->* . (or/c string? boolean?)) - (let ([result (next-pages element pmap)]) - (and result (first result)))) - -(module+ test - (check-equal? (next-page 'foo test-pmap) "bar") - (check-equal? (next-page 'one test-pmap) "two") - (check-false (next-page 'three test-pmap))) - -;; convert path to pmap-key -;; used for converting "here" values to pmap-keys -(define/contract (->pmap-key x) - (any/c . -> . pmap-key?) - (->string (remove-all-ext (last (explode-path (->path x)))))) - -(module+ test - (check-equal? (->pmap-key "bar") "bar") - (check-equal? (->pmap-key "foo/bar") "bar") - (check-equal? (->pmap-key "foo/bar.html") "bar") - (check-equal? (->pmap-key "/Users/this/that/foo/bar.html.pp") "bar")) - - -;; convert key to URL -;; = key name + suffix of template (or suffix of default template) -;; todo: finish this function, right now it just appends html -;; this would also be useful for start page (showing correct url of generated pages) -(define/contract (pmap-key->url key) - (pmap-key? . -> . string?) - (string-append key ".html")) - diff --git a/pollen-file-tools.rkt b/pollen-file-tools.rkt index 918e602..65b4b43 100644 --- a/pollen-file-tools.rkt +++ b/pollen-file-tools.rkt @@ -119,13 +119,13 @@ ; or a file (e.g., html) that has a pollen source file (ormap (λ(proc) (proc (->path x))) (list pollen-source? has-pollen-source?))) -(define/contract (pmap-source? x) +(define/contract (ptree-source? x) (any/c . -> . boolean?) - (has-ext? (->path x) POLLEN_MAP_EXT)) + (has-ext? (->path x) POLLEN_TREE_EXT)) (module+ test - (check-true (pmap-source? "foo.pmap")) - (check-false (pmap-source? "pmap.bar"))) + (check-true (ptree-source? "foo.ptree")) + (check-false (ptree-source? "ptree.bar"))) (define/contract (pollen-source? x) diff --git a/predicates.rkt b/predicates.rkt index 93b9e79..9ed1499 100644 --- a/predicates.rkt +++ b/predicates.rkt @@ -162,29 +162,29 @@ (check-false (elements-unique? "foo"))) -;; certain pmap requirements are enforced at compile-time. -;; (such as pmap-keys must be valid strings, and unique.) +;; certain ptree requirements are enforced at compile-time. +;; (such as pnodes must be valid strings, and unique.) ;; otherwise this becomes a rather expensive contract -;; because every function in pmap.rkt uses it. -;; note that a pmap is just a bunch of recursively nested pmaps. -(define/contract (pmap? x) +;; because every function in ptree.rkt uses it. +;; note that a ptree is just a bunch of recursively nested ptrees. +(define/contract (ptree? x) (any/c . -> . boolean?) (and (match x ;; a tagged-xexpr with one attr ('parent) ;; whose subelements recursively meet the same test. - [(list (? pmap-key? tag) (? pmap-attr? attr) elements ...) - (andmap pmap? elements)] + [(list (? pnode? tag) (? ptree-attr? attr) elements ...) + (andmap ptree? elements)] [else #f]))) (module+ test - (check-true (pmap? '(foo ((parent "bar"))))) - (check-false (pmap? '(foo))) - (check-false (pmap? '(foo ((parent "bar")(hee "haw"))))) - (check-true (pmap? '(foo ((parent "bar")) (hee ((parent "foo")))))) - (check-false (pmap? '(foo ((parent "bar")) (hee ((uncle "foo"))))))) - -;; pmap attr must be ((parent "value")) -(define/contract (pmap-attr? x) + (check-true (ptree? '(foo ((parent "bar"))))) + (check-false (ptree? '(foo))) + (check-false (ptree? '(foo ((parent "bar")(hee "haw"))))) + (check-true (ptree? '(foo ((parent "bar")) (hee ((parent "foo")))))) + (check-false (ptree? '(foo ((parent "bar")) (hee ((uncle "foo"))))))) + +;; ptree attr must be ((parent "value")) +(define/contract (ptree-attr? x) (any/c . -> . boolean?) (define foo 'bar) (match x @@ -193,36 +193,36 @@ [else #f])) (module+ test - (check-true (pmap-attr? '((parent "bar")))) - (check-false (pmap-attr? '((parent "bar") '(foo "bar")))) - (check-false (pmap-attr? '()))) + (check-true (ptree-attr? '((parent "bar")))) + (check-false (ptree-attr? '((parent "bar") '(foo "bar")))) + (check-false (ptree-attr? '()))) -;; pmap location must represent a possible valid filename -(define/contract (pmap-key? x #:loud [loud #f]) +;; ptree location must represent a possible valid filename +(define/contract (pnode? x #:loud [loud #f]) ((any/c) (#:loud boolean?) . ->* . boolean?) - ;; todo: how to express the fact that the pmap-location must be + ;; todo: how to express the fact that the ptree-location must be ;; a valid base name for a file? ;; however, don't restrict it to existing files - ;; (author may want to use pmap as wireframe) + ;; (author may want to use ptree as wireframe) (define result (or (eq? x #f) ; OK for map-key to be #f (and (or (symbol? x) (string? x)) ;; todo: should test be same as valid module name? (->boolean (regexp-match #px"^[-_A-Za-z0-9]+$" (->string x)))))) (if (and (not result) loud) - (error "Not a valid pmap key:" x) + (error "Not a valid ptree key:" x) result)) (module+ test - (check-true (pmap-key? #f)) - (check-true (pmap-key? "foo-bar")) - (check-true (pmap-key? "Foo_Bar_0123")) - (check-true (pmap-key? 'foo-bar)) - (check-false (pmap-key? "foo-bar.p")) - (check-false (pmap-key? "/Users/MB/foo-bar")) - (check-false (pmap-key? "")) - (check-false (pmap-key? " "))) + (check-true (pnode? #f)) + (check-true (pnode? "foo-bar")) + (check-true (pnode? "Foo_Bar_0123")) + (check-true (pnode? 'foo-bar)) + (check-false (pnode? "foo-bar.p")) + (check-false (pnode? "/Users/MB/foo-bar")) + (check-false (pnode? "")) + (check-false (pnode? " "))) ;; recursive whitespace test diff --git a/pmap-decode.rkt b/ptree-decode.rkt similarity index 50% rename from pmap-decode.rkt rename to ptree-decode.rkt index e9d82c8..7ff7fe4 100644 --- a/pmap-decode.rkt +++ b/ptree-decode.rkt @@ -7,13 +7,13 @@ (provide (all-defined-out)) ;; These functions need to be separated so that they can be accessed by pollen parser (in main.rkt) -;; Other pmap functions are in pmap.rkt. -;; pmap decoder takes pmap source and returns a full pmap structure. +;; Other ptree functions are in ptree.rkt. +;; ptree decoder takes ptree source and returns a full ptree structure. -;; recursively processes map, converting map locations & their parents into xexprs of this shape: +;; recursively processes tree, converting tree locations & their parents into xexprs of this shape: ;; '(location ((parent "parent"))) (define/contract (add-parents x [parent empty]) - ((tagged-xexpr?) (xexpr-tag?) . ->* . pmap?) + ((tagged-xexpr?) (xexpr-tag?) . ->* . ptree?) (match x ;; this pattern signifies next level in hierarchy ;; where first element is new parent, and rest are children. @@ -22,38 +22,38 @@ ;; xexpr with tag as name, parent as attr, children as elements with tag as next parent (make-tagged-xexpr tag attr (map (λ(c) (add-parents c tag)) children)))] ;; single map entry: convert to xexpr with parent - [else (make-tagged-xexpr (->symbol x) (make-xexpr-attr POLLEN_MAP_PARENT_KEY (->string parent)))])) + [else (make-tagged-xexpr (->symbol x) (make-xexpr-attr POLLEN_TREE_PARENT_NAME (->string parent)))])) ;; this sets default input for following functions -(define/contract (pmap-root->pmap tx) - ;; (not/c pmap) prevents pmaps from being accepted as input - ((and/c tagged-xexpr? (not/c pmap?)) . -> . pmap?) +(define/contract (ptree-root->ptree tx) + ;; (not/c ptree) prevents ptrees from being accepted as input + ((and/c tagged-xexpr? (not/c ptree?)) . -> . ptree?) (add-parents tx)) (module+ test - (define test-pmap-main `(pmap-main "foo" "bar" (one (two "three")))) - (check-equal? (pmap-root->pmap test-pmap-main) - `(pmap-main ((,POLLEN_MAP_PARENT_KEY "")) (foo ((,POLLEN_MAP_PARENT_KEY "pmap-main"))) (bar ((,POLLEN_MAP_PARENT_KEY "pmap-main"))) (one ((,POLLEN_MAP_PARENT_KEY "pmap-main")) (two ((,POLLEN_MAP_PARENT_KEY "one")) (three ((,POLLEN_MAP_PARENT_KEY "two")))))))) + (define test-ptree-main `(ptree-main "foo" "bar" (one (two "three")))) + (check-equal? (ptree-root->ptree test-ptree-main) + `(ptree-main ((,POLLEN_TREE_PARENT_NAME "")) (foo ((,POLLEN_TREE_PARENT_NAME "ptree-main"))) (bar ((,POLLEN_TREE_PARENT_NAME "ptree-main"))) (one ((,POLLEN_TREE_PARENT_NAME "ptree-main")) (two ((,POLLEN_TREE_PARENT_NAME "one")) (three ((,POLLEN_TREE_PARENT_NAME "two")))))))) -;; contract for pmap-source-decode -(define/contract (valid-pmap-keys? x) +;; contract for ptree-source-decode +(define/contract (valid-pnodes? x) (any/c . -> . boolean?) - (andmap (λ(x) (pmap-key? #:loud #t x)) (filter-not whitespace? (flatten x)))) + (andmap (λ(x) (pnode? #:loud #t x)) (filter-not whitespace? (flatten x)))) -;; contract for pmap-source-decode -(define/contract (unique-pmap-keys? x) +;; contract for ptree-source-decode +(define/contract (unique-pnodes? x) (any/c . -> . boolean?) ;; use map ->string to make keys comparable (elements-unique? #:loud #t (map ->string (filter-not whitespace? (flatten x))))) -(define/contract (pmap-source-decode . elements) - (() #:rest (and/c valid-pmap-keys? unique-pmap-keys?) . ->* . pmap?) - (pmap-root->pmap (decode (cons POLLEN_MAP_ROOT_NAME elements) +(define/contract (ptree-source-decode . elements) + (() #:rest (and/c valid-pnodes? unique-pnodes?) . ->* . ptree?) + (ptree-root->ptree (decode (cons POLLEN_TREE_ROOT_NAME elements) #:xexpr-elements-proc (λ(xs) (filter-not whitespace? xs))))) diff --git a/ptree.rkt b/ptree.rkt new file mode 100644 index 0000000..de0934c --- /dev/null +++ b/ptree.rkt @@ -0,0 +1,239 @@ +#lang racket/base +(require xml xml/path racket/list racket/string racket/contract racket/match racket/set) +(require "tools.rkt" "world.rkt" "ptree-decode.rkt" "debug.rkt") + +(module+ test (require rackunit)) + +(provide (all-defined-out)) + +;; function to set up the project-ptree. +;; this is to make life simpler when using tree navigation functions. +;; the current main.ptree of the project is used as the default input. +;; without this, you'd have to pass it over and over. +;; which is sort of the functional lifestyle, +;; but in templates, gets tiresome and error-prone. +(define/contract (make-project-ptree) + (-> ptree?) + (define ptree-source (build-path START_DIR DEFAULT_POLLEN_TREE)) + (if (file-exists? ptree-source) + ;; Load it from default path. + ;; dynamic require of a ptree source file gets you a full ptree. + (begin + (message "Loading ptree file" (->string ptree-source)) + (dynamic-require ptree-source POLLEN_ROOT)) + ;; ... or else synthesize it + (let* ([files (directory-list START_DIR)] + ;; restrict files to those with pollen extensions + [files (map remove-ext (filter (λ(x) (has-ext? x POLLEN_SOURCE_EXT)) files))]) + ;; make a POLLEN_TREE_ROOT_NAME structure and convert it to a full ptree + (message "Generating ptree from file listing") + (ptree-root->ptree (cons POLLEN_TREE_ROOT_NAME (map path->string files)))))) + +(define project-ptree (make-project-ptree)) + + +;; remove parents from tree (i.e., just remove attrs) +;; is not the inverse of add-parents, i.e., you do not get back your original input. +(define/contract (remove-parents mt) + (ptree? . -> . tagged-xexpr?) + (remove-attrs mt)) + +(module+ test + (check-equal? (remove-parents + `(ptree-main ((,POLLEN_TREE_PARENT_NAME "")) (foo ((,POLLEN_TREE_PARENT_NAME ""))) (bar ((,POLLEN_TREE_PARENT_NAME ""))) (one ((,POLLEN_TREE_PARENT_NAME "")) (two ((,POLLEN_TREE_PARENT_NAME "one")) (three ((,POLLEN_TREE_PARENT_NAME "two"))))))) + '(ptree-main (foo) (bar) (one (two (three)))))) + + +(module+ test + (let ([sample-main `(POLLEN_TREE_ROOT_NAME "foo" "bar" (one (two "three")))]) + (check-equal? (ptree-root->ptree sample-main) + `(POLLEN_TREE_ROOT_NAME ((,POLLEN_TREE_PARENT_NAME "")) (foo ((,POLLEN_TREE_PARENT_NAME "POLLEN_TREE_ROOT_NAME"))) (bar ((,POLLEN_TREE_PARENT_NAME "POLLEN_TREE_ROOT_NAME"))) (one ((,POLLEN_TREE_PARENT_NAME "POLLEN_TREE_ROOT_NAME")) (two ((,POLLEN_TREE_PARENT_NAME "one")) (three ((,POLLEN_TREE_PARENT_NAME "two"))))))))) + + + +;; return the parent of a given name +(define/contract (parent node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c string? boolean?)) + (and node (let ([result (se-path* `(,(->symbol node) #:parent) ptree)]) + (and result (->string result))))) ; se-path* returns #f if nothing found + + +(module+ test + (define test-ptree-main `(ptree-main "foo" "bar" (one (two "three")))) + (define test-ptree (ptree-root->ptree test-ptree-main)) + (check-equal? (parent 'three test-ptree) "two") + (check-equal? (parent "three" test-ptree) "two") + (check-false (parent 'nonexistent-name test-ptree))) + + + +; get children of a particular node +(define/contract (children node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c list? boolean?)) + ;; se-path*/list returns '() if nothing found + (and node (let ([children (se-path*/list `(,(->symbol node)) ptree)]) + ; If there are sublists, just take first node + (and (not (empty? children)) (map (λ(i) (->string (if (list? i) (car i) i))) children))))) + +(module+ test + (check-equal? (children 'one test-ptree) (list "two")) + (check-equal? (children 'two test-ptree) (list "three")) + (check-false (children 'three test-ptree)) + (check-false (children 'fooburger test-ptree))) + + +;; find all siblings on current level: go up to parent and ask for children +(define/contract (siblings node [ptree project-ptree]) + ;; this never returns false: node is always a sibling of itself. + ;; todo: how to use input value in contract? e.g., to check that node is part of output list + ((pnode?) (ptree?) . ->* . (or/c list? boolean?)) + (children (parent node ptree) ptree)) + +(module+ test + (check-equal? (siblings 'one test-ptree) '("foo" "bar" "one")) + (check-equal? (siblings 'foo test-ptree) '("foo" "bar" "one")) + (check-equal? (siblings 'two test-ptree) '("two")) + (check-false (siblings 'invalid-key test-ptree))) + + + +(define/contract (siblings-split node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (values (or/c (listof pnode?) boolean?) + (or/c (listof pnode?) boolean?))) + (let-values ([(left right) (splitf-at (siblings node ptree) + (λ(e) (not (equal? (->string e) (->string node)))))]) + (values (if (empty? left) #f left) (if (empty? (cdr right)) #f (cdr right))))) + +(module+ test + (check-equal? (values->list (siblings-split 'one test-ptree)) '(("foo" "bar") #f)) + (check-equal? (values->list (siblings-split 'bar test-ptree)) (list '("foo") '("one")))) + + +;; siblings to the left of target node (i.e., precede in tree order) +(define (siblings-left node [ptree project-ptree]) + (let-values ([(left right) (siblings-split node ptree)]) + left)) + +(module+ test + (check-equal? (siblings-left 'one test-ptree) '("foo" "bar")) + (check-false (siblings-left 'foo test-ptree))) + +;; siblings to the right of target node (i.e., follow in tree order) +(define (siblings-right node [ptree project-ptree]) + (let-values ([(left right) (siblings-split node ptree)]) + right)) + +(module+ test + (check-false (siblings-right 'one test-ptree)) + (check-equal? (siblings-right 'foo test-ptree) '("bar" "one"))) + + +;; get node immediately to the left in tree +(define/contract (sibling-previous node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c string? boolean?)) + (let ([siblings (siblings-left node ptree)]) + (and siblings (last siblings)))) + +(module+ test + (check-equal? (sibling-previous 'bar test-ptree) "foo") + (check-false (sibling-previous 'foo test-ptree))) + +;; get node immediately to the right in tree +(define/contract (sibling-next node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c string? boolean?)) + (let ([siblings (siblings-right node ptree)]) + (and siblings (first siblings)))) + +(module+ test + (check-equal? (sibling-next 'foo test-ptree) "bar") + (check-false (sibling-next 'one test-ptree))) + + +;; flatten tree to sequence +(define/contract (all-pages [ptree project-ptree]) + (ptree? . -> . (listof string?)) + ; use cdr to get rid of root tag at front + (map ->string (cdr (flatten (remove-parents ptree))))) + +(module+ test + (check-equal? (all-pages test-ptree) '("foo" "bar" "one" "two" "three"))) + +;; helper function for get-previous-pages and get-next-pages +(define/contract (adjacent-pages side node [ptree project-ptree]) + ((symbol? pnode?) (ptree?) . ->* . (or/c list? boolean?)) + (let ([result ((if (equal? side 'left) + takef + takef-right) (all-pages ptree) + (λ(y) (not (equal? (->string node) (->string y)))))]) + (and (not (empty? result)) result))) + +(module+ test + (check-equal? (adjacent-pages 'left 'one test-ptree) '("foo" "bar")) + (check-equal? (adjacent-pages 'left 'three test-ptree) '("foo" "bar" "one" "two")) + (check-false (adjacent-pages 'left 'foo test-ptree))) + + +;; get sequence of earlier pages +(define/contract (previous-pages node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c list? boolean?)) + (adjacent-pages 'left node ptree)) + +(module+ test + (check-equal? (previous-pages 'one test-ptree) '("foo" "bar")) + (check-equal? (previous-pages 'three test-ptree) '("foo" "bar" "one" "two")) + (check-false (previous-pages 'foo test-ptree))) + + +;; get sequence of next pages +(define (next-pages node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c list? boolean?)) + (adjacent-pages 'right node ptree)) + +(module+ test + (check-equal? (next-pages 'foo test-ptree) '("bar" "one" "two" "three")) + (check-equal? (next-pages 'one test-ptree) '("two" "three")) + (check-false (next-pages 'three test-ptree))) + +;; get page immediately previous +(define/contract (previous-page node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c string? boolean?)) + (let ([result (previous-pages node ptree)]) + (and result (last result)))) + +(module+ test + (check-equal? (previous-page 'one test-ptree) "bar") + (check-equal? (previous-page 'three test-ptree) "two") + (check-false (previous-page 'foo test-ptree))) + +;; get page immediately next +(define (next-page node [ptree project-ptree]) + ((pnode?) (ptree?) . ->* . (or/c string? boolean?)) + (let ([result (next-pages node ptree)]) + (and result (first result)))) + +(module+ test + (check-equal? (next-page 'foo test-ptree) "bar") + (check-equal? (next-page 'one test-ptree) "two") + (check-false (next-page 'three test-ptree))) + +;; convert path to pnode +;; used for converting "here" values to pnodes +(define/contract (here->pnode x) + (pathish? . -> . pnode?) + (->string (remove-all-ext (last (explode-path (->path x)))))) + +(module+ test + (check-equal? (here->pnode "bar") "bar") + (check-equal? (here->pnode "foo/bar") "bar") + (check-equal? (here->pnode "foo/bar.html") "bar") + (check-equal? (here->pnode "/Users/this/that/foo/bar.html.pp") "bar")) + + +;; convert key to URL +;; = key name + suffix of template (or suffix of default template) +;; todo: finish this function, right now it just appends html +;; this would also be useful for start page (showing correct url of generated pages) +(define/contract (pnode->url key) + (pnode? . -> . string?) + (string-append key ".html")) + diff --git a/regenerate.rkt b/regenerate.rkt index 4d6cb83..66f2144 100644 --- a/regenerate.rkt +++ b/regenerate.rkt @@ -1,7 +1,7 @@ #lang racket/base (require racket/list racket/path racket/port racket/system racket/file racket/rerequire racket/contract racket/bool) -(require "world.rkt" "tools.rkt" "pmap.rkt" "readability.rkt" "template.rkt") +(require "world.rkt" "tools.rkt" "ptree.rkt" "readability.rkt" "template.rkt") (module+ test (require rackunit)) @@ -105,15 +105,16 @@ (() (#:force boolean?) #:rest (listof pathish?) . ->* . void?) (define (®enerate x) (let ([path (->complete-path (->path x))]) + ; (message "Regenerating" (->string path)) (cond ;; this will catch pp (preprocessor) files [(needs-preproc? path) (regenerate-with-preproc path #:force force)] ;; this will catch p files, ;; and files without extension that correspond to p files [(needs-template? path) (regenerate-with-template path #:force force)] - ;; this will catch pmap (pollen map) files - [(pmap-source? path) (let ([pmap (dynamic-require path 'main)]) - (regenerate-with-pmap pmap #:force force))] + ;; this will catch ptree files + [(ptree-source? path) (let ([ptree (dynamic-require path 'main)]) + (regenerate-with-ptree ptree #:force force))] [(equal? FALLBACK_TEMPLATE_NAME (->string (file-name-from-path path))) (message "Regenerate: using fallback template")] [(file-exists? path) 'pass-through] @@ -276,6 +277,8 @@ source-reloaded?) (begin (store-refresh-in-mod-dates source-path template-path) + (message "Rendering source" (->string source-path) + "with template" (->string template-path)) (let ([page-result (render-source-with-template source-path template-path)]) (display-to-file #:exists 'replace page-result output-path) (regenerated-message (file-name-from-path output-path)))) @@ -307,17 +310,17 @@ (namespace-require 'racket) ; use namespace-require for FIRST require, then eval after ;; for include-template (used below) (eval '(require web-server/templates) (current-namespace)) - ;; for pmap navigation functions, and template commands - (eval '(require (planet mb/pollen/pmap)(planet mb/pollen/template)) (current-namespace)) + ;; for ptree navigation functions, and template commands + (eval '(require (planet mb/pollen/debug)(planet mb/pollen/ptree)(planet mb/pollen/template)) (current-namespace)) ;; import source into eval space. This sets up main & metas (eval `(require ,(path->string source-name)) (current-namespace)) (eval `(include-template #:command-char ,TEMPLATE_FIELD_DELIMITER ,(->string template-name)) (current-namespace)))) -;; regenerate files listed in a pmap file -(define/contract (regenerate-with-pmap pmap #:force [force #f]) - ((pmap?) (#:force boolean?) . ->* . void?) +;; regenerate files listed in a ptree file +(define/contract (regenerate-with-ptree ptree #:force [force #f]) + ((ptree?) (#:force boolean?) . ->* . void?) ;; pass force parameter through - (for-each (λ(i) (regenerate i #:force force)) (all-pages pmap))) + (for-each (λ(i) (regenerate i #:force force)) (all-pages ptree))) diff --git a/server-routes.rkt b/server-routes.rkt index aeabdb3..18b3a81 100644 --- a/server-routes.rkt +++ b/server-routes.rkt @@ -79,11 +79,11 @@ ;; First, generate some lists of files. ;; get lists of files by mapping a filter function for each file type - (define-values (pollen-files preproc-files pmap-files template-files) + (define-values (pollen-files preproc-files ptree-files template-files) (let ([all-files-in-project-directory (directory-list pollen-file-root)]) (apply values (map (λ(test) (filter test all-files-in-project-directory)) - (list pollen-source? preproc-source? pmap-source? template-source?))))) + (list pollen-source? preproc-source? ptree-source? template-source?))))) ;; The actual post-preproc files may not have been generated yet ;; so calculate their names (rather than rely on directory list) @@ -123,14 +123,14 @@ `(td (a ((href ,target)) ,name)))) `(tr ,(make-link-cell 'direct) ,@(map make-link-cell routes))) - (if (andmap empty? (list pmap-files all-pollen-files all-preproc-files template-files)) + (if (andmap empty? (list ptree-files all-pollen-files all-preproc-files template-files)) '(body "No files yet. Get to work!") `(body (style ((type "text/css")) "td a { display: block; width: 100%; height: 100%; padding: 8px; }" "td:hover {background: #eee}") (table ((style "font-family:Concourse T3;font-size:115%")) - ;; options for pmap files and template files - ,@(map (λ(file) (make-file-row file '(raw))) (append pmap-files template-files)) + ;; options for ptree files and template files + ,@(map (λ(file) (make-file-row file '(raw))) (append ptree-files template-files)) ;; options for pollen files ,@(map (λ(file) (make-file-row file '(raw source xexpr force))) post-pollen-files) @@ -151,5 +151,5 @@ (define request-url (request-uri req)) (define path (reroot-path (url->path request-url) pollen-file-root)) (define force (equal? (get-query-value request-url 'force) "true")) - (with-handlers ([exn:fail? (λ(e) (message "Default route ignoring" (url->string request-url)))]) + (with-handlers ([exn:fail? (λ(e) (message "Default route ignoring" (url->string request-url) "because of error\n" (exn-message e)))]) (regenerate path #:force force))) \ No newline at end of file diff --git a/server.rkt b/server.rkt index 15c3e76..339ceaa 100755 --- a/server.rkt +++ b/server.rkt @@ -1,14 +1,17 @@ #lang web-server +(require "start.rkt") (require web-server/servlet-env) (require web-server/dispatch web-server/dispatchers/dispatch) (require xml) (require "server-routes.rkt" "predicates.rkt" "debug.rkt") -(message "Pollen server starting...") -(message "Racket version" (version)) +(message "Starting webserver") (define (logger req) - (message (url->string (request-uri req)) "from" (request-client-ip req))) + (define client (request-client-ip req)) + (when (equal? client "::1") + (set! client "localhost")) + (message "Request:" (url->string (request-uri req)) "from" client)) (define/contract (route-wrapper route-proc) (procedure? . -> . procedure?) diff --git a/start.rkt b/start.rkt new file mode 100644 index 0000000..6e19f56 --- /dev/null +++ b/start.rkt @@ -0,0 +1,4 @@ +#lang racket/base +(require "debug.rkt") +(message "Starting Pollen") +(message "Using Racket" (version)) diff --git a/world.rkt b/world.rkt index 1d248c9..ae1a9d8 100644 --- a/world.rkt +++ b/world.rkt @@ -1,5 +1,7 @@ #lang racket/base +(provide (all-defined-out)) + (define POLLEN_PREPROC_EXT 'pp) (define POLLEN_SOURCE_EXT 'p) (define TEMPLATE_FILE_PREFIX "-") @@ -10,10 +12,10 @@ (define FALLBACK_TEMPLATE_NAME "-temp-fallback-template.html") (define TEMPLATE_META_KEY "template") -(define POLLEN_MAP_EXT 'pmap) -(define DEFAULT_POLLEN_MAP "main.pmap") -(define POLLEN_MAP_PARENT_KEY 'parent) -(define POLLEN_MAP_ROOT_NAME 'pmap-root) +(define POLLEN_TREE_EXT 'ptree) +(define DEFAULT_POLLEN_TREE "main.ptree") +(define POLLEN_TREE_PARENT_NAME 'parent) +(define POLLEN_TREE_ROOT_NAME 'ptree-root) (define MAIN_POLLEN_EXPORT 'main) ;(define META_POLLEN_TAG 'metas) @@ -28,14 +30,10 @@ (define OUTPUT_SUBDIR 'public) -(define RACKET_PATH "/Applications/Racket/bin/racket") +(define RACKET_PATH "/usr/bin/racket") (define POLLEN_ROOT 'main) -; todo: this doesn't work as hoped -;(define-syntax POLLEN_ROOT_TAG -; (λ(stx) (datum->syntax stx 'main))) - ; get the starting directory, which is the parent of 'run-file (define START_DIR (let-values ([(dir ignored also-ignored) @@ -45,4 +43,3 @@ dir))) -(provide (all-defined-out)) \ No newline at end of file