documentation updates plus related bugfixes

pull/111/head
Matthew Butterick 9 years ago
parent f00ad0d0c8
commit fbaf2c98d4

@ -1,8 +1,7 @@
#lang racket/base
(require (for-syntax racket/base syntax/strip-context racket/syntax "../setup.rkt" "split-metas.rkt")
"to-string.rkt" "../pagetree.rkt" "splice.rkt" "../setup.rkt") ; need world here to resolve PARSER-MODE-ARG
"to-string.rkt" "../pagetree.rkt" "splice.rkt" "../setup.rkt" ) ; need world here to resolve PARSER-MODE-ARG
(provide (all-defined-out))
(require sugar/debug)
(define-syntax-rule (define+provide-module-begin-in-mode PARSER-MODE-ARG)
(begin
@ -46,7 +45,7 @@
[(eq? parser-mode 'MODE-PAGETREE) decode-pagetree]
[(eq? parser-mode 'MODE-MARKUP) (λ(xs) (apply ROOT xs))] ; if `root` undefined, it becomes a default tag function
[(eq? parser-mode 'MODE-MARKDOWN)
(λ(xs) (apply ROOT ((dynamic-require 'markdown 'parse-markdown) (apply string-append (map to-string xs)))))]
(λ(xs) (apply ROOT (map strip-empty-attrs ((dynamic-require 'markdown 'parse-markdown) (apply string-append (map to-string xs))))))]
[else (λ(xs) (apply string-append (map to-string xs)))])] ; string output for preprocessor
;; drop leading newlines, as they're often the result of `defines` and `requires`
[doc-elements (or (memf (λ(ln) (not (equal? ln NEWLINE))) DOC-RAW) null)]

@ -9,11 +9,11 @@
(define spliceable? (λ(x) (and (pair? x) (eq? (car x) splicing-tag))))
(define not-null-string? (λ(x) (not (and (string? x) (= (string-length x) 0)))))
(let loop ([x x])
(if (list? x)
(apply append (map (λ(x) ((if (spliceable? x)
cdr
list) (loop x))) (filter not-null-string? x)))
x)))
(if (list? x)
(apply append (map (λ(x) ((if (spliceable? x)
cdr
list) (loop x))) (filter not-null-string? x)))
x)))
(module+ test
(require rackunit)
@ -22,4 +22,19 @@
(check-equal? (splice `((,splice-signal-tag 1 (,splice-signal-tag 2 "" (,splice-signal-tag 3 (div 4 (,splice-signal-tag 5))) 6) "" 7)))
'(1 2 3 (div 4 5) 6 7))
(check-equal? (splice `((,splice-signal-tag "foo" "" "bar"))) '("foo" "bar"))
(check-equal? (splice null) null))
(check-equal? (splice null) null))
(define (strip-empty-attrs x)
(let loop ([x x])
(if (list? x)
;; this will strip all empty lists.
;; in practice, they would only appear in attrs position
(map loop (filter (λ(x) (not (null? x))) x))
x)))
(module+ test
(check-equal? (strip-empty-attrs '(p ())) '(p))
(check-equal? (strip-empty-attrs '(p () "foo")) '(p "foo"))
(check-equal? (strip-empty-attrs '(p () (em () "foo") "bar")) '(p (em "foo") "bar")))

@ -7,21 +7,21 @@
(define (meta? x) ; meta has form (define-meta key value)
(and (list? x) (>= (length x) 3) (eq? (car x) meta-key)))
(define (non-meta?/gather x)
(or (not (meta? x))
(and (set! matches (cons x matches)) #f)))
(define rest
(let loop ([x (if (list? tree) tree (list tree))])
(if (list? x)
(map loop (filter non-meta?/gather x))
x)))
(cond
[(meta? x) (set! matches (cons x matches)) #f]
[(list? x) (filter (λ(x) x) (map loop x))]
[else x])))
(values (apply hasheq (apply append (map cdr matches))) rest))
(values (apply hasheq (apply append (reverse (map cdr matches)))) rest))
(module+ test
(require rackunit)
(define x '(root (div (define-meta foo "bar") "hi") "zim" "zam"))
(define-values (metas rest) (split-metas x 'define-meta))
(check-equal? metas '#hasheq((foo . "bar")))
(check-equal? rest '(root (div "hi") "zim" "zam")))
(let-values ([(metas rest) (split-metas '(root (div (define-meta foo "bar") "hi") "zim" (define-meta foo "boing") "zam") 'define-meta)])
(check-equal? metas '#hasheq((foo . "boing")))
(check-equal? rest '(root (div "hi") "zim" "zam")))
(let-values ([(metas rest) (split-metas '(root (define-meta dog "Roxy") (define-meta dog "Lex")) 'define-meta)])
(check-equal? metas '#hasheq((dog . "Lex"))))
(let-values ([(metas rest) (split-metas '(root (define-meta dog "Roxy") (div (define-meta dog "Lex"))) 'define-meta)])
(check-equal? metas '#hasheq((dog . "Lex")))))

@ -6,4 +6,8 @@
[(string? x) x]
[(or (null? x) (void? x)) ""]
[(or (symbol? x) (number? x) (path? x) (char? x)) (format "~a" x)]
;; special handling for procedures, because if a procedure reaches this func,
;; it usually indicates a failed attempt to use a tag function.
;; meaning, it's more useful to raise an error.
[(procedure? x) (error 'pollen "Can't convert procedure ~a to string" x)]
[else (format "~v" x)]))

@ -1 +1 @@
1454367371
1454721844

@ -2,7 +2,7 @@
@title{Acknowledgments}
One curious aspect of free software is that you can appropriate the benefits of other people's work while making it look like your own. No such legerdemain here. Whatever effort I've put into Pollen is dwarfed by the epic accomplishments of the Racket development team. I thank all of them — especially @link["https://www.cs.utah.edu/~mflatt/"]{Matthew Flatt}, @link["http://faculty.cs.byu.edu/~jay/home/"]{Jay McCarthy}, and @link["http://www.ccs.neu.edu/home/matthias/"]{Matthias Felleisen} — for making this tremendous tool available, for adding several features I suggested, and for patiently answering my dumb questions over the months.
One curious aspect of free software is that you can appropriate the benefits of other people's work while making it look like your own. No such legerdemain here. Whatever effort I've put into Pollen is dwarfed by the epic accomplishments of the Racket development team. I thank all of them — especially @link["http://www.barzilay.org"]{Eli Barzilay}, @link["https://www.cs.utah.edu/~mflatt/"]{Matthew Flatt}, @link["http://faculty.cs.byu.edu/~jay/home/"]{Jay McCarthy}, @link["https://www.eecs.northwestern.edu/~robby/"]{Robby Findler}, @link["https://www.soic.indiana.edu/faculty-research/directory/profile.html?profile_id=311"]{Sam Tobin-Hochstadt}, and @link["http://www.ccs.neu.edu/home/matthias/"]{Matthias Felleisen} — for making this tremendous tool available, for adding several features I suggested, and for patiently answering my dumb questions over the months.
Thank you to Greg Hendershott for his Markdown parser.

@ -2,7 +2,7 @@
@title[#:tag "big-picture"]{The big picture}
A summary of the key components & concepts of the Pollen publishing system and how they fit together. If you've completed the @secref["quick-tour"], this will lend some context to what you saw. The next tutorials will make more sense if you read this first.
A summary of the key components & concepts of the Pollen publishing system and how they fit together. If you've completed the @secref["quick-tour"], this will lend some context to what you saw. The upcoming @seclink["first-tutorial"]{tutorials} will make more sense if you read this first.
@section[#:tag "the-book-is-a-program"]{The book is a program}
@ -35,13 +35,13 @@ This is the core design principle of Pollen. Consistent with this principle, Pol
@section{Development environment}
The Pollen development environment has three main pieces: the DrRacket code editor, the project server, and the command-line tool.
The Pollen development environment has three main pieces: the DrRacket code editor, the project server, and the command line.
@itemlist[
@item{@bold{Edit source files with DrRacket.} DrRacket is Racket's GUI code editor. Sure, you can also use a generic text editor. But DrRacket lets you immediately run your source and see if it works.}
@item{@bold{Preview & test web pages with the Pollen project server.} Pollen has a built-in development web server called the @defterm{project server}. After you start the project server, you can preview your web pages within any web browser, allowing you to test them with maximum accuracy.}
@item{@bold{Preview & test web pages with the Pollen project server.} Pollen has a built-in development web server called the @seclink["Using_the_project_server"]{@defterm{project server}}. After you start the project server, you can preview your web pages within any web browser, allowing you to test them with maximum accuracy.}
@item{@bold{Write the docs.} The project server can recognize and render Scribble files, so you can use it as a previewing tool while you're writing your documentation.}
@ -53,7 +53,7 @@ The Pollen development environment has three main pieces: the DrRacket code edit
@section{A special data structure for HTML}
Unlike other programming languages, Pollen (and Racket) internally represent HTML with something called @secref["X-expressions" #:doc '(lib "pollen/scribblings/pollen.scrbl")]. An X-expression is simply a list that represents an HTML @defterm{element}, meaning a thing with an opening tag, a closing tag, and content in between. Like HTML elements, X-expressions can be nested. Unlike HTML elements, X-expressions have no closing tag, they use parentheses to denote the start and end, and text elements are put inside quotes.
Unlike other programming languages, Pollen (and Racket) internally represent HTML with something called @secref["X-expressions"]. An X-expression is simply a list that represents an HTML @defterm{element}, meaning a thing with an opening tag, a closing tag, and content in between. Like HTML elements, X-expressions can be nested. Unlike HTML elements, X-expressions have no closing tag, they use parentheses to denote the start and end, and text elements are put inside quotes.
For example, consider this HTML element:
@ -63,7 +63,7 @@ As a Racket X-expression, this would be written:
@nested[#:style 'code-inset]{@verbatim{(body (h1 "Hello world") (p "Nice to " (i "see") " you."))}}
More will be said about X-expressions. But a couple advantages should be evident already. First, without the redundant angle brackets, the X-expression is arguably more readable than the equivalent HTML. Second, an X-expression is preferable to treating HTML as a simple string, because it preserves the internal structure of the element.
More will be said about X-expressions. But a several advantages should be evident already. First, without the redundant angle brackets, the X-expression is arguably more readable than the equivalent HTML. Second, an X-expression is preferable to treating HTML as a simple string, because it preserves the internal structure of the element. Third, an X-expression is a native data type in Racket.
@section{Pollen command syntax}
@ -75,10 +75,10 @@ As mentioned above, a Pollen source file is not code with text embedded in it, b
@item{@bold{If you can write text, you can program in Pollen.} Really. As you already found out in the @secref["quick-tour"], this is a valid Pollen program:
@codeblock{
#lang pollen
Hello setup: how are you on this fine summer day?
Bonjour, tout le monde: comment ça va?
}}
@item{@bold{Commands start with ◊.} A simple rule: if a piece of text starts with @litchar{◊}, it's treated as a command; otherwise it's treated as ordinary text.}
@item{@bold{Commands start with ◊.} A simple rule: if something in a Pollen source file starts with @litchar{◊}, it's treated as a command; otherwise it's treated as ordinary text.}
@item{@bold{Write commands in Pollen mode or Racket mode.} Commands can use two equivalent notation systems: either Pollen's text-oriented command syntax, or standard Racket syntax.}
@ -108,7 +108,7 @@ If you want to apply a particular page format to multiple sources of content —
@itemlist[
@item{@bold{Templates can be any format.} Usually Pollen templates will be HTML. But they don't have to be.}
@item{@bold{Templates can be any format.} Usually Pollen templates will be HTML. But they don't have to be. Templates can generate any kind of file — either text-based (XML) or not (PDF).}
@item{@bold{Markdown authoring mode.} Pollen has a built-in Markdown parser, so you can import Markdown sources into a Pollen publication.}

@ -1,7 +1,7 @@
#lang scribble/manual
@(require scribble/bnf scribble/eval "utils.rkt" "mb-tools.rkt"
(for-syntax racket/base)
(for-label rackunit pollen/core pollen/setup pollen/render pollen/template (only-in scribble/reader
(for-label rackunit pollen/core pollen/setup pollen/cache pollen/tag pollen/render pollen/template (only-in scribble/reader
use-at-readtable)))
@(define read-eval (make-base-eval))
@ -17,23 +17,25 @@
Pollen uses a special character — the @italic{lozenge}, which looks like this: ◊ — to mark commands within a Pollen source file. So when you put a ◊ in your source, whatever comes next will be treated as a command. If you don't, it will just be interpreted as plain text.
@section{The lozenge glyph (◊)}
@section[#:tag "the-lozenge"]{The lozenge (◊)}
I chose the lozenge as the command character because a) it appears in almost every font, b) it's barely used in ordinary typesetting, c) it's not used in any programming language that I know of, and d) its shape and color allow it to stand out easily in code without being distracting.
Here's how you type it:
If you're using DrRacket, you can use the @onscreen{Insert Command Char} button at the top of the editing window to — you guessed it — insert the command character.
@bold{Mac}: option + shift + V
If you're using a different editor, here's how you type it:
@bold{Windows}: holding down alt, type 9674 on the num pad
@bold{Mac}: option + shift + V
@(linebreak)@bold{Windows}: holding down alt, type 9674 on the num pad
@(linebreak)@bold{Ubuntu}: ctrl + shift + U, then 25CA
@bold{Ubuntu}: ctrl + shift + U, then 25CA
@subsection{``But I don't want to use it ...''}
Still, if you don't want to use the lozenge as your command character, you can set Pollen's @racket[default-command-char] value to whatever character you want (see also @seclink["setup-overrides"]).
Fine, but you have to pick @italic{something} as your command character. If you don't like this one, you can override it within a project — see @seclink["setup-overrides"].
@margin-note{Scribble uses the @"@" sign as a delimiter. It's not a bad choice if you only work with Racket files. But as you use Pollen to work on other kinds of text-based files that commonly contain @"@" signs — HTML pages especially — it gets cumbersome. So I changed it.}
Still, it's not like I'm asking you to learn @link["http://c2.com/cgi/wiki?AplLanguage"]{APL}. Racket supports Unicode, so it's a little silly to artificially limit ourselves to ASCII.
But don't knock the lozenge till you try it.
My advice: don't knock the lozenge till you try it.
@subsection{Lozenge helpers}
@ -42,7 +44,7 @@ But don't knock the lozenge till you try it.
When you use DrRacket, you'll see a button in the toolbar that says @onscreen{Insert command char}. This will insert the lozenge (or whatever command character you've defined for your project).
@subsubsection{AHK script}
@subsubsection{AHK script for Windows}
Courtesy of @link["https://github.com/maphew"]{Matt Wilkie}: ``Here's a working AHK script to have double-tap backtick send the lozenge character. It took way more time than I want to think about, once started I couldn't let go.''
@ -103,25 +105,26 @@ Courtesy of @link["https://github.com/lerichard95"]{Richard Le}: ``If you're usi
;; Bind key to M-\ a la DrRacket for lambda
(global-set-key "\M-\\" 'insert-lozenge)}
@;--------------------------------------------------------------------
@section[#:tag "the-two-command-modes"]{The two command modes: Pollen mode & Racket mode}
@section[#:tag "the-two-command-styles"]{The two command styles: Pollen style & Racket style}
Pollen commands can be entered in one of two modes: @italic{Pollen mode} or @italic{Racket mode}. Both modes start with a lozenge (@litchar["◊"]):
Pollen commands can be entered in one of two styles: @italic{Pollen style} or @italic{Racket style}. Both styles start with a lozenge (@litchar["◊"]):
@racketblock[
@#,BNF-seq[@litchar["◊"] @nonterm{command name} @litchar{[} @nonterm{Racket arguments ...} @litchar{]} @litchar["{"] @nonterm{text argument} @litchar["}"]]
@#,BNF-seq[@litchar["◊"] @nonterm{command name} @litchar{[} @nonterm{Racket arguments ...} @litchar{]} @litchar["{"] @nonterm{text body ...} @litchar["}"]]
@#,BNF-seq[@litchar["◊"]
@litchar{(} @nonterm{Racket expression} @litchar{)}]
]
@bold{Pollen-mode commands}
@bold{Pollen-style commands}
A Pollen-mode command has the three possible parts after the @litchar["◊"]:
A Pollen-style command has the three possible parts after the @litchar["◊"]:
@itemlist[
@item{The @italic{command name} appears immediately after the @litchar["◊"]. Typically it's a short word.}
@item{The @italic{Racket arguments} appear between square brackets. Pollen is partly an interface to the Racket programming language. These arguments are entered using Racket conventions — e.g., a @code{string of text} needs to be put in quotes as a @code{"string of text"}. If you like programming, you'll end up using these frequently. If you don't, you won't.}
@item{The @italic{text argument} appears between braces (aka curly brackets). You can put any ordinary text here. Unlike with the Racket arguments, you don't put quotes around the text.}
@item{The @italic{Racket arguments} appear between square brackets. Pollen is partly an interface to the Racket programming language. These arguments are entered using Racket conventions — e.g., a string of text needs to be put in quotes as a @code{"string of text"}. If you like programming, you'll end up using these arguments frequently. If you don't, you won't.}
@item{The @italic{text body} appears between braces (aka curly brackets). You can put any ordinary text here. Unlike with the Racket arguments, you don't put quotes around the text.}
]
Each of the three parts is optional. You can also nest commands within each other. However:
@ -131,7 +134,7 @@ Each of the three parts is optional. You can also nest commands within each othe
@item{Whatever parts you use must always appear in the order above.}
]
Here are a few examples of correct Pollen-mode commands:
Here are a few examples of correct Pollen-style commands:
@codeblock{
#lang pollen
@ -147,40 +150,40 @@ And some incorrect examples:
@codeblock{
#lang pollen
◊tag {Text inside the tag.} ; space between first and second parts
◊tag[Text inside the tag] ; text argument needs to be within braces
◊tag[Text inside the tag] ; text body needs to be within braces
◊tag{Text inside the tag}[#:attr "value"] ; wrong order
}
The next section describes each of these parts in detail.
@bold{Racket-mode commands}
@bold{Racket-style commands}
If you're familiar with Racket expressions, you can use the Racket-mode commands to embed them within Pollen source files. It's simple: any Racket expression can become a Pollen command by adding @litchar["◊"] to the front. So in Racket, this code:
If you're familiar with Racket expressions, you can use the Racket-style commands to embed them within Pollen source files. It's simple: any Racket expression becomes a Pollen command by adding @litchar["◊"] to the front. So in Racket, this code:
@codeblock{
#lang racket
(define song "Revolution")
(format "~a #~a" song (* 3 3))
#lang racket
(define band "Level")
(format "~a ~a" band (* 2 3 7))
}
Can be converted to Pollen like so:
@codeblock{
#lang pollen
◊(define song "Revolution")
◊(format "~a #~a" song (* 3 3))
#lang pollen
◊(define band "Level")
◊(format "~a ~a" band (* 2 3 7))
}
And in DrRacket, they produce the same output:
@repl-output{Revolution #9}
@repl-output{Level 42}
Beyond that, there's not much to say about Racket mode — any valid expression you can write in Racket will also be a valid Racket-mode Pollen command.
Beyond that, there's not much to say about Racket style — any valid Racket expression will also be a valid Racket-style Pollen command.
@bold{The relationship of Pollen mode and Racket mode}
@bold{The relationship of Pollen style and Racket style}
Even if you don't plan to write a lot of Racket-mode commands, you should be aware that under the hood, Pollen is converting all commands in Pollen mode to Racket mode. So a Pollen-mode command that looks like this:
Even if you don't plan to write a lot of Racket-style commands, you should be aware that under the hood, Pollen is converting all Pollen-style commands to Racket style. So a Pollen-style command that looks like this:
@codeblock[#:keep-lang-line? #f]{
#lang pollen
@ -188,38 +191,39 @@ Even if you don't plan to write a lot of Racket-mode commands, you should be awa
}
Is actually being turned into a Racket-mode command like this:
Is actually being turned into this Racket-style command:
@codeblock[#:keep-lang-line? #f]{
#lang racket
(headline #:size 'enormous "Man Bites Dog!")
}
Thus a Pollen-mode command is just an alternate way of writing a Racket-mode command. (More broadly, all of Pollen is just an alternate way of using Racket.)
Thus a Pollen-style command is just an alternate way of writing a Racket-style command. (More broadly, all of Pollen is just an alternate way of using Racket.)
The corollary is that you can always write Pollen commands using whichever mode is more convenient or readable. For instance, the earlier example, written in the Racket mode:
The corollary is that you can always write Pollen commands using whichever style is more convenient or readable. For instance, the earlier example, written in the Racket style:
@codeblock{
#lang pollen
◊(define song "Revolution")
◊(format "~a #~a" song (* 3 3))
◊(define band "Level")
◊(format "~a ~a" band (* 2 3 7))
}
Can be rewritten using Pollen mode:
Can be rewritten in Pollen style:
@codeblock{
#lang pollen
◊define[song]{Revolution}
◊format["~a #~a" song (* 3 3)]
◊define[band]{Level}
◊format["~a ~a" band (* 2 3 7)]
}
And it will work the same way.
You can combine the two styles in whatever way makes sense to you. I typically reserve Pollen-style commands for when I'm mixing commands into textual material. Meaning, I prefer @code{◊headline[#:size 'enormous]{Man Bites Dog!}} over @code{◊(headline #:size 'enormous "Man Bites Dog!")}. But when I'm writing or using traditional Racket functions, I find Racket-style commands to be more readable (because they correspond to ordinary Racket syntax, and thus can be moved between Pollen and Racket source files more easily). So I prefer @code{◊(define band "Level")} over @code{◊define[band]{Level}}.
@;--------------------------------------------------------------------
@subsection{The command name}
In Pollen, you'll typically use the command name for one of four purposes:
In Pollen, you'll likely use a command for one of these purposes:
@itemlist[
@item{To invoke a tag function.}
@ -229,10 +233,11 @@ In Pollen, you'll typically use the command name for one of four purposes:
@item{To insert a comment.}
]
@;--------------------------------------------------------------------
Let's look at each kind of use.
@subsubsection{Invoking tag functions}
By default, Pollen treats every command name as a @italic{tag function}. The default tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content.
By default, Pollen treats every command name as a @italic{tag function}. The default tag function creates a @seclink["What_s_a_txexpr_" #:doc '(lib "txexpr/scribblings/txexpr.scrbl")]{tagged X-expression} with the command name as the tag, and the text body as the content.
@codeblock{
#lang pollen
@ -246,10 +251,10 @@ To streamline markup, Pollen doesn't restrict you to a certain set of tags, nor
@codeblock{
#lang pollen
◊utterlyridiculoustagname{Oh really?}
◊utterly-ridiculous-tag-name{Oh really?}
}
@repl-output{'(utterlyridiculoustagname "Oh really?")}
@repl-output{'(utterly-ridiculous-tag-name "Oh really?")}
@ -270,7 +275,6 @@ the expected number of arguments does not match the given number
What to do? Read on.
@;--------------------------------------------------------------------
@subsubsection{Invoking other functions}
Though every command name starts out as a default tag function, it doesn't necessarily end there. You have two options for invoking other functions: defining your own, or invoking others from Racket.
@ -296,7 +300,7 @@ We can define @code{strong} to do something else, like add to the text:
@repl-output{'(strong "Hey! Listen up! Fancy Sauce, $1")}
The replacement function has to accept any arguments that might get passed along, but it doesn't have to do anything with them. For instance, this function definition won't work because @code{strong} is going to get a text argument that it's not defined to handle:
The replacement function has to accept any arguments that might get passed along, but it doesn't have to do anything with them. For instance, this function definition won't work because @code{strong} is going to get a text body that it's not defined to handle:
@codeblock{
#lang pollen
@ -322,6 +326,9 @@ Whereas in this version, @code{strong} accepts an argument called @code{text}, b
@repl-output{'(fib "1 1 2 3 5 8 13 ...")}
@margin-note{The text body can pass an indefinite number of arguments. A well-designed tag function should be able to handle them, unlike these synthetic examples. For a more realistic example, see @secref["the-text-body"].}
You can attach any behavior to a command name. As your project evolves, you can also update the behavior of a command name. In that way, Pollen commands become a set of hooks to which you can attach more elaborate processing.
@bold{Using Racket functions}
@ -330,19 +337,19 @@ You aren't limited to functions you define. Any function from Racket, or any Rac
@codeblock|{
#lang pollen
range[1 20]
(range 1 20)
}|
@repl-output{'(range 1 20)}
Hold on — that's not what we want. Where's the list of numbers? The problem here is that we didn't explicitly import the @racketmodname[racket/list] library, which contains the definition for @racket[range]. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without @racketmodname[racket/list], Pollen just thinks we're trying to use @code{range} as a tag function (and if we had been, then @repl-output{'(range 1 20)} would've been the right result).
Hold on — that's not what we want. Where's the list of numbers? The problem here is that we for to import the @racketmodname[racket/list] library, which contains the definition for @racket[range]. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without @racketmodname[racket/list], Pollen just thinks we're trying to use @code{range} as a tag function (and if we had been, then @val['(range 1 20)] would've been the right result).
We fix this by using the @racket[require] command to bring in the @racketmodname[racket/list] library, which contains the @racket[range] we want:
@codeblock|{
#lang pollen
◊(require racket/list)
range[1 20]
(range 1 20)
}|
@repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}
@ -353,7 +360,7 @@ Of course, you can also invoke Racket functions indirectly, by attaching them to
#lang pollen
◊(require racket/list)
◊(define (rick start finish) (range start finish))
rick[1 20]
(rick 1 20)
}|
@repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}
@ -361,7 +368,7 @@ Of course, you can also invoke Racket functions indirectly, by attaching them to
Let's return to the problem that surfaced in the last section — the fact that some command names can't be used as tag functions because they're already being used for other things. You can work around this by defining your own tag function with a non-conflicting name.
For instance, suppose we want to use @code{map} as a tag even though Racket is using it for its own function called @racket[map]. First, we invent a command name that doesn't conflict. Let's call it @code{my-map}. As you learned above, Pollen will treat a new command name as a tag function by default:
For instance, suppose we want to use @code{map} as a tag even though Racket is using it for its own function called @racket[map]. First, we invent a command name that doesn't conflict. Let's call it @id{my-map}. As you learned above, Pollen will treat a new command name as a tag function by default:
@codeblock|{
#lang pollen
@ -371,7 +378,7 @@ For instance, suppose we want to use @code{map} as a tag even though Racket is u
@repl-output{'(my-map "How I would love this to be a map.")}
But @code{my-map} is not the tag we want. We need to define @code{my-map} to be a tag function for @code{map}. We can do this with the Pollen helper @racket[default-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too:
But @id{my-map} is not the tag we want. We need to define @id{my-map} to be a tag function for @id{map}. We can do this with the Pollen helper @racket[default-tag-function]. That function lives in @racketmodname[pollen/tag], so we @racket[require] that too:
@codeblock|{
@ -387,7 +394,6 @@ Problem solved.
@;--------------------------------------------------------------------
@subsubsection{Inserting the value of a variable}
A Pollen command name usually refers to a function, but it can also refer to a @italic{variable}, which is a data value. Once you define the variable, you can insert it into your source by using the ◊ notation without any other arguments:
@ -401,9 +407,7 @@ The value of foo is ◊foo
@repl-output{The value of foo is bar}
Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. This won't work, because a variable is not a function:
@margin-note{To understand what happens here, recall the relationship between Pollen's command modes. The Pollen-mode command @code{◊foo[]} becomes the Racket-mode command @code{(foo)}, which after variable substitution becomes @code{("bar")}. If you try to evaluate @code{("bar")} — e.g., in DrRacket — you'll get the same error.}
Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. For instance, this next example won't work, because a variable is not a function:
@codeblock|{
@ -419,7 +423,10 @@ expected a procedure that can be applied to arguments
  arguments...: [none]}
The reason we can simply drop @code{◊foo} into the text argument of another Pollen command is that the variable @code{foo} holds a string (i.e., a text value).
To understand what happens here, recall the relationship between Pollen's command styles. The Pollen-style command @code{◊foo[]} becomes the Racket-style command @code{(foo)}, which after variable substitution becomes @code{("bar")}. If you try to evaluate @code{("bar")} — e.g., in DrRacket — you'll get the same error.
The reason we can simply insert @code{◊foo} into the text body of another Pollen command is that the variable @code{foo} holds a string (i.e., a text value).
In preprocessor source files, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted:
@ -431,7 +438,7 @@ The value of zam is ◊zam
@repl-output{The value of zam is 42}
@margin-note{In an unsaved DrRacket file, or a file without a special Pollen source extension, the @tt{#lang pollen} designation invokes the Pollen preprocessor by default. You can explicitly invoke preprocessor mode by starting a file with @tt{#lang pollen/pre}. See also @secref["Preprocessor___pp_extension_"].}
@margin-note{In an unsaved DrRacket file, or a file without a special Pollen source extension, the @tt{#lang pollen} designation invokes the Pollen preprocessor by default. You can explicitly invoke preprocessor style by starting a file with @tt{#lang pollen/pre}. See also @secref["Preprocessor___pp_extension_"].}
If the variable holds a container datatype (like a @racket[list], @racket[hash], or @racket[vector]), Pollen will produce the Racket text representation of the item. Here, @code{zam} is a @racket[list] of integers:
@ -449,7 +456,7 @@ This feature is included for your convenience. But in general, your readers won'
#lang pollen
◊(require racket/string)
◊(define zam (list 1 2 3))
The value of zam is ◊string-join[(map number->string zam)]{ and }
The value of zam is ◊(string-join (map number->string zam) " and ")
}|
@repl-output{The value of zam is 1 and 2 and 3}
@ -462,9 +469,9 @@ Pollen will still produce an error if you try to convert an esoteric value to a
The value of zam is ◊zam
}|
@errorblock{Pollen decoder: can't convert #<procedure:+> to string}
@errorblock{pollen: Can't convert procedure #<procedure:+> to string}
Moreover, Pollen will not perform @italic{any} automatic text conversion in Pollen markup source files. Suppose we take the example above — which worked as a preprocessor source file — and change the language to @racket[pollen/markup]:
In Pollen markup, the result is different. A Pollen markup file makes an X-expression, not text, so Pollen doesn't perform @italic{any} automatic text conversion — that's your job. Suppose we take the example above — which worked with the Pollen preprocessor — and change the language to @racketmodname[pollen/markup]:
@codeblock|{
#lang pollen/markup
@ -478,6 +485,19 @@ This time, the file will produce an error:
pollen markup error: in '(root "The value of zam is " (1 2 3)), '(1 2 3) is not a valid element (must be txexpr, string, symbol, XML char, or cdata)
}
But the second example above, with the explicit conversion using @racket[string-join], does work in Pollen markup, because strings are valid X-expressions:
@codeblock|{
#lang pollen/markup
◊(require racket/string)
◊(define zam (list 1 2 3))
The value of zam is ◊(string-join (map number->string zam) " and ")
}|
@repl-output{'(root "The value of zam is " "1 and 2 and 3")}
@margin-note{See @secref["File_formats"] for more about the differences between Pollen dialects.}
One special case to know about. In the examples above, there's a word space between the variable and the other text. But suppose you need to insert a variable into text so that there's no space in between. The simple ◊ notation won't work, because it won't be clear where the variable name ends and the text begins.
For instance, suppose we want to use a variable @code{edge} next to the string @code{px}:
@ -488,7 +508,7 @@ For instance, suppose we want to use a variable @code{edge} next to the string
p { margin-left: ◊edgepx; }
}|
@errorblock{Pollen decoder: can't convert #<procedure:...t/pollen/tag.rkt:6:2> to string}
@errorblock{pollen: Can't convert procedure #<procedure:pollen-tag:edgepx> to string}
The example fails because Pollen reads the whole string after the @litchar{◊} as the single variable name @code{edgepx}. Since @code{edgepx} isn't defined, it's treated as a tag function, and since Pollen can't convert a function to a string, we get an error.
@ -515,21 +535,20 @@ The value of edge is ◊|edge| pixels}
@;--------------------------------------------------------------------
@subsubsection{Inserting metas}
@italic{Metas} are keyvalue pairs embedded in a source file that are not included in the main output when the source is compiled. Rather, they're gathered and exported as a separate hash table called, unsurprisingly, @racket[metas]. This hashtable is a good place to store information about the document that you might want to use later (for instance, a list of topic categories that the document belongs to).
@italic{Metas} are keyvalue pairs embedded in a source file that are not included in the main output when the source is compiled. Rather, they're gathered and exported as a separate hash table called, unsurprisingly, @id{metas}. This hashtable is a good place to store information about the document that you might want to use later (for instance, a list of topic categories that the document belongs to).
@margin-note{Pollen occasionally uses metas internally. For instance, the @racket[get-template-for] function will look in the metas of a source file to see if a template is explicitly specified. The @racket[pollen/template] module also contains functions for working with metas, such as @racket[select-from-metas].}
@margin-note{Pollen occasionally uses metas internally. For instance, the @racket[get-template-for] function will look in the metas of a source file to see if a template is explicitly specified. The @racketmodname[pollen/core] module also contains functions for working with metas, such as @racket[select-from-metas].}
To make a meta, you create a tag with the special @racket[define-meta] name. Then you have two choices: you can either embed the key-value pair as an attribute, or as a tagged X-expression within the meta (using the key as the tag, and the value as the body):
@codeblock{
#lang pollen
◊define-meta[dog]{Roxy} ; Pollen-mode syntax
◊define-meta[dog]{Roxy} ; Pollen-style syntax
◊some-tag[#:key "value"]{Normal tag}
◊(define-meta cat "Chopper") ; equivalent Racket-mode syntax
◊(define-meta cat "Chopper") ; equivalent Racket-style syntax
◊some-tag[#:key "value"]{Another normal tag}
}
@ -549,7 +568,7 @@ Second, the metas are collected into a hash table that is exported with the name
'#hasheq((dog . "Roxy") (cat . "Chopper") (here-path . "unsaved-editor"))
}
The only key that's automatically defined in every meta table is @code{here-path}, which is the absolute path to the source file. (In this case, because the file hasn't been saved, you'll see the @code{unsaved-editor} name instead.)
The only key that's automatically defined in every meta table is @id{'here-path}, which is the absolute path to the source file. (In this case, because the file hasn't been saved, you'll see the @val{unsaved-editor} name instead.)
Still, you can override this too:
@ -571,7 +590,7 @@ When you run this code, the result will be the same as before, but this time the
}
It doesn't matter how many metas you put in a source file, nor where you put them. They'll all be extracted into the @code{metas} hash table. The order of the metas is not preserved (because order is not preserved in a hash table). But if you have two metas with the same key, the later one will supersede the earlier one:
It doesn't matter how many metas you put in a source file, nor where you put them. They'll all be extracted into the @id{metas} hash table. The order of the metas is not preserved (because order is not preserved in a hash table). But if you have two metas with the same key, the later one will supersede the earlier one:
@codeblock{
#lang pollen
@ -579,28 +598,28 @@ It doesn't matter how many metas you put in a source file, nor where you put the
◊(define-meta dog "Lex")
}
In this case, though there are two metas named @racket[dog] (and they use different forms) only the second one persists:
Though there are two metas named @id{dog}, only the second one persists:
@terminal{
> metas
'#hasheq((dog . "Lex") (here-path . "unsaved-editor"))
}
@bold{Pro tip}: the @racket[metas] hashtable is available when you import a Pollen source file in the usual way, but it's also made available through a submodule called, unsurprisingly, @racket[metas].
@codeblock{
#lang racket/base
(require "pollen-source.rkt") ; doc and metas and everything else
(require (submod "pollen-source.rkt" metas)) ; just metas
}
The @racket[metas] submodule is useful because it gives you access to the @racket[metas] hashtable @italic{without} compiling the rest of the file. So if you need to collect metas from a set of source files — for instance, page titles (for a table of contents) or categories — getting the metas through the submodule is likely to be faster.
@subsubsection{Retrieving metas}
The @id{metas} hashtable is available immediately within the body of your source file. You can use @racket[select] to get values out of @id{metas}.
@;--------------------------------------------------------------------
@subsubsection{Retrieving metas}
@codeblock{
#lang pollen
◊(define-meta dog "Roxy")
◊(select 'dog metas)
}
The @racket[metas] hashtable is available immediately within the body of your source file. You can use @racket[hash-ref] to get values out of @racket[metas].
@repl-output{Roxy}
@id{metas} is an immutable hash, so you can also use immutable-hash functions, like @racket[hash-ref]:
@codeblock{
#lang pollen
@ -608,48 +627,51 @@ The @racket[metas] hashtable is available immediately within the body of your so
◊(hash-ref metas 'dog)
}
@terminal{
Roxy
}
@repl-output{Roxy}
Because the metas are collected first, you can actually invoke a meta before you define it:
@codeblock{
#lang pollen
◊(hash-ref metas 'dog)
◊(select 'dog metas)
◊(define-meta dog "Roxy")
◊(define-meta dog "Spooky")
}
@terminal{
Spooky
}
@repl-output{Spooky}
This can be useful for setting up fields that you want to include in @racket[metas] but also have visible in the body of a document, like a title.
This can be useful for setting up fields that you want to include in @id{metas} but also have visible in the body of a document, like a title.
@codeblock{
#lang pollen/markup
◊(define-meta title "The Amazing Truth")
◊h1{◊(hash-ref metas 'title)}
◊h1{◊(select 'title metas)}
}
The result of this file will be:
@terminal{
'(root (h1 "The Amazing Truth"))
}
@repl-output{'(root (h1 "The Amazing Truth"))}
And the metas:
@terminal{
> metas
'#hasheq((title . "The Amazing Truth") (here-path . "unsaved-editor"))
}
You cannot, however, use @racket[hash-set!] or other similar functions, because @racket[metas] is an immutable hash.
@bold{Pro tip}: Within Pollen, the fastest way to get a @id{metas} hashtable from another source file is to use @racket[cached-metas].
@bold{Pro tip #2}: Outside Pollen, the @id{metas} hashtable is available when you import a Pollen source file in the usual way, but it's also made available through a submodule called, unsurprisingly, @id{metas}.
@codeblock{
#lang racket/base
(require "pollen-source.rkt") ; doc and metas and everything else
(require (submod "pollen-source.rkt" metas)) ; just metas
}
The @id{metas} submodule gives you access to the @id{metas} hashtable @italic{without} compiling the rest of the file. So if you need to harvest metas from a set of source files — for instance, page titles (for a table of contents) or categories — using @racket[require] with the submodule will be faster.
@;--------------------------------------------------------------------
@subsubsection{Inserting a comment}
Two options.
@ -679,10 +701,9 @@ Actually, it's all a comment now
@repl-output{Actually, it's all a comment now}
@;--------------------------------------------------------------------
@subsection{The Racket arguments}
The middle part of a Pollen-mode command contains the @italic{Racket arguments} @litchar{[}between square brackets.@litchar{]} Most often, you'll see these used to pass extra information to commands that operate on text.
The middle part of a Pollen-style command contains the @italic{Racket arguments} @litchar{[}between square brackets.@litchar{]} Most often, you'll see these used to pass extra information to commands that operate on text.
For instance, tag functions. Recall from before that any not-yet-defined command name in Pollen is treated as a tag function:
@ -709,7 +730,7 @@ Here's the hard way. You can type out your list of attributes in Racket format a
@repl-output{'(title ((class "red") (id "first")) "The Beginning of the End")}
But that's a lot of parentheses to think about. So here's the easy way. Anytime you use a tag function, there's a shortcut for inserting attributes. You can enter them as a series of @italic{keyword arguments} between the Racket-argument brackets. The only caveat is that the values for these keyword arguments have to be strings. So taken together, they look like this:
But that's a lot of parentheses to think about. So here's the easy way. Whenever you use a tag function, there's a shortcut for inserting attributes. You can enter them as a series of @italic{keyword arguments} between the Racket-argument brackets. The only caveat is that the values for these keyword arguments have to be strings. So taken together, they look like this:
@codeblock|{
#lang pollen
@ -722,7 +743,7 @@ The string arguments can be any valid Racket expressions that produce strings. F
@codeblock|{
#lang pollen
◊title[#:class (format "~a" (* 6 7)) #:id "first"]{The Beginning of the End}
◊title[#:class (number->string (* 6 7)) #:id "first"]{The Beginning of the End}
}|
@repl-output{'(title ((class "42") (id "first")) "The Beginning of the End")}
@ -754,10 +775,29 @@ When used in custom tag functions, keyword arguments don't have to represent att
'(h6 "Trivial league")
}
@;--------------------------------------------------------------------
@subsection{The text argument}
@bold{Pro tip}: See also @racket[define-tag-function], which automatically converts keyword arguments into attributes before they reach your function:
@codeblock|{
#lang pollen
◊(require pollen/tag)
◊(define-tag-function (heading attrs elems)
(define level (cadr (assq 'level attrs)))
`(,(string->symbol (format "h~a" level)) ,@elems))
◊heading[#:level 1]{Major league}
◊heading[#:level 2]{Minor league}
◊heading[#:level 6]{Trivial league}
}|
@repl-output{'(h1 "Major league")
'(h2 "Minor league")
'(h6 "Trivial league")
}
@subsection[#:tag "the-text-body"]{The text body}
The third part of a Pollen-mode command is the text argument. The text argument @litchar{@"{"}appears between curly braces@litchar{@"}"}. It can contain any text you want. The text argument can also contain other Pollen commands with their own text arguments. And they can contain other Pollen commands ... and so on, all the way down.
The third part of a Pollen-style command is the text body. The text body @litchar{@"{"}appears between curly braces@litchar{@"}"}. It can contain any text you want. The text body can also contain other Pollen commands with their own text body. And they can contain other Pollen commands ... and so on, all the way down.
@codeblock|{
#lang pollen
@ -766,9 +806,9 @@ The third part of a Pollen-mode command is the text argument. The text argument
@repl-output{'(div "Do it again. " (div "And again. " (div "And yet again.")))}
Three small details to know about the text argument.
Three things to know about the text body.
First, the only character that needs special handling in a text argument is the lozenge @litchar{◊}. A lozenge ordinarily marks a new command. So if you want an actual lozenge to appear in the text, you have to escape it by typing @litchar{◊"◊"}.
First, the only character that needs special handling in the text body is the lozenge @litchar{◊}. A lozenge ordinarily marks a new command. So if you want an actual lozenge to appear in the text, you have to escape it by typing @litchar{◊"◊"}.
@codeblock|{
#lang pollen
@ -777,7 +817,7 @@ First, the only character that needs special handling in a text argument is the
@repl-output{'(definition "This is the lozenge: ◊")}
Second, the whitespace-trimming policy. Here's the short version: if there's a carriage return at either end of the text argument, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text argument, with carriage returns on the ends:
Second, the whitespace-trimming policy. Here's the short version: if there's a newline at either end of the text body, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text body, with newlines on the ends:
@codeblock|{
#lang pollen
@ -790,7 +830,7 @@ I agree.
@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
Yields the same result as this one:
Yields the same result as this one, without the newlines:
@codeblock|{
#lang pollen
@ -801,10 +841,11 @@ I agree.}
@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
For the long version, please see [future link: Spaces, Newlines, and Indentation].
For the long version, please see @secref["Spaces__Newlines__and_Indentation"
#:doc '(lib "scribblings/scribble/scribble.scrbl")].
Third, within a multiline text argument, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That's why in the last example, we got this:
Third, within a multiline text body, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That's why in the last example, we got this:
@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
@ -812,7 +853,7 @@ Instead of this:
@repl-output{'(div "Roomy!\n\nI agree.")}
Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that operates on multiline text arguments needs to be able to handle an indefinite number of strings. For instance, this @code{jejune} function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:
Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that might operate on a multiline text body needs to be able to handle an indefinite number of strings. For instance, this @code{jejune} function only accepts a single argument. It will work with a single-line text body, because that produces a single string:
@codeblock|{
#lang pollen
@ -823,7 +864,7 @@ Under most circumstances, these two tagged X-expressions will behave the same wa
@repl-output{'(jejune "Irrational confidence")}
But watch what happens with a multiline text argument:
But watch what happens with a multiline text body:
@codeblock|{
#lang pollen
@ -909,13 +950,11 @@ For numeric entities, you can also use a four-digit Unicode hex number by prefac
Of course, you don't need to use @racket[string->symbol] and @racket[string->number] directly in your source. You can also define tag functions that generate entities. The key point is that to be treated as an entity, the return value must be a symbol or number, rather than a string.
@section{Adding Pollen-mode commands to a Racket file}
@section{Adding Pollen-style commands to a Racket file}
@defmodulelang[pollen/mode]
Just as you can embed any Racket-mode command in a Pollen source file, you can go the other way and embed Pollen-mode commands in a Racket file. For instance, in your @secref["The__pollen_rkt__file"], you may find it convenient to use Pollen mode for certain values.
You enable Pollen mode within your source file by adding @racketmodname[pollen/mode] to your @tt{#lang} line at the top of your source:
Just as you can embed any Racket-style command in a Pollen source file, you can go the other way and embed Pollen-style commands in a Racket file. Just insert @racketmodname[pollen/mode] in the @tt{#lang} line at the top of your source:
@fileblock["pollen.rkt" @codeblock{
#lang pollen/mode racket/base
@ -926,12 +965,12 @@ You enable Pollen mode within your source file by adding @racketmodname[pollen/m
(define (home-link)
(link #:href "index.html" "Click to go home"))
(define (home-link-pollen-mode)
(define (home-link-pollen-style)
◊link[#:href "index.html"]{Click to go home})
}]
Here, both @tt{(home-link)} and @tt{(home-link-pollen-mode)} will produce the same X-expression as a result:
Here, both @tt{(home-link)} and @tt{(home-link-pollen-style)} will produce the same X-expression as a result:
@terminal{'(a ((href "index.html")) "Click to go home")}
@ -939,11 +978,9 @@ Of course, you can use @racketmodname[pollen/mode] in any Racket source file, no
@bold{Major caveat}: @racketmodname[pollen/mode] only works with Pollen's default command character, namely the lozenge (@litchar{◊}). If you've overridden this command character in your @filepath{pollen.rkt} file, your custom command character will work everywhere @italic{except} in @racketmodname[pollen/mode]. This limitation is necessary to prevent the intractable situation where @filepath{pollen.rkt} relies on @racketmodname[pollen/mode], but @racketmodname[pollen/mode] relies on a config setting in @filepath{pollen.rkt}.
Also keep in mind that @racketmodname[pollen/mode] is just a syntactic convenience. It doesn't change any of the underlying semantics of your Racket source file. Your Pollen-mode commands are being translated into Racket commands and compiled along with everything else.
Another good way to use Pollen-mode commands in Racket is for unit tests with @racketmodname[rackunit]. With @racketmodname[pollen/mode], you can write your unit tests in Pollen mode or Racket mode (or mix them).
Also keep in mind that @racketmodname[pollen/mode] is just a syntactic convenience. It doesn't change any of the underlying semantics of your Racket source file. Your Pollen-style commands are being translated into Racket commands and compiled along with everything else.
@margin-note{Unit tests are little one-line tests you put into your code to verify that it does what you expect. You make these with the @racketmodname[rackunit] library, which is beloved by all Racket programmers. For more, see @secref["quick-start" #:doc '(lib "rackunit/scribblings/rackunit.scrbl")].}
Another good way to use Pollen-style commands in Racket is for unit tests with @racketmodname[rackunit]. With @racketmodname[pollen/mode], you can write your unit tests in Pollen style or Racket style (or mix them). This makes it easy to verify that Pollen-style commands will turn into the Racket values that you expect:
@fileblock["pollen.rkt" @codeblock|{
#lang pollen/mode racket/base

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
#lang scribble/manual
@(require scribble/eval pollen/render pollen/setup (for-label racket (except-in pollen #%module-begin) pollen/setup sugar pollen/pagetree))
@(require scribble/eval pollen/render "mb-tools.rkt" pollen/setup (for-label pollen/cache (except-in pollen/top #%top def/c) racket (except-in pollen #%module-begin) pollen/setup sugar pollen/pagetree))
@(define my-eval (make-base-eval))
@(my-eval `(require pollen pollen/file))
@ -14,11 +14,11 @@
@defmodulelang*[(pollen/pre pollen/markdown pollen/markup pollen/ptree)]
The Pollen language is divided into variants, or @italic{dialects}, that are tailored to suit each of the core source formats.
The Pollen language is divided into @italic{dialects} that are tailored to suit each of the core source formats.
These dialects can be invoked one of two ways: either by invoking a specific dialect in the first line of the file (also known as the @tt{#lang} line), or by using the generic @tt{#lang pollen} as the first line, and then the correct dialect will be automatically selected based on the source file extension.
If the @tt{#lang} line specifies a dialect different from the one specified by the file extension, the @tt{#lang} line will take precedence.
If the @tt{#lang} line specifies a dialect different from the one implied by the file extension, the @tt{#lang} line will take precedence.
For ease of use, the behavior of the Pollen language departs from the standard Racket language in several ways. The differences are noted below.
@ -27,28 +27,34 @@ For ease of use, the behavior of the Pollen language departs from the standard R
Commands must start with the special lozenge character @litchar{◊}. Other material is interpreted as plain text. See @secref["pollen-command-syntax"] for more.
You can change the command character for a project by overriding @racket[default-command-char].
@bold{How is this different from Racket?} In Racket, everything is a command, and plain text must be quoted.
@subsection{Any command is valid}
There are no undefined commands in Pollen. If a command has not already been defined, it's treated as a tag function. See @secref["pollen-command-syntax"] for more.
There are no undefined commands in Pollen. If a command has not already been defined, it's treated as a tag function. See @secref["pollen-command-syntax"] and @racketmodname[pollen/top] for more.
@bold{How is this different from Racket?} In Racket, if you try to treat an identifier as a function before defining it with @racket[define], you'll get an error.
@subsection{Standard exports}
By default, every Pollen source file exports two identifiers, which you can access by using the source file with @racket[require]:
By default, every Pollen source file exports two identifiers:
The main export, @racket[doc], contains the output of the file. The type of output depends on the source format (documented below).
@itemlist[
@item{@id{doc} contains the output of the file. The type of output depends on the source format (about which, more below).}
The second export, @racket[metas], is a hashtable of keyvalue pairs with extra information that is extracted from the source. These @racket[metas] will always contain the key @racket['here-path], which returns a string representation of the full path to the source file. Beyond that, the only @racket[metas] are the ones that are specified within the source file (see the source formats below for more detail on how to specify metas).
@item{@id{metas} is a hashtable of keyvalue pairs with extra information that is extracted from the source. These @id{metas} will always contain the key @id{'here-path}, which returns a string representation of the full path to the source file. Beyond that, the only @id{metas} are the ones that are specified within the source file (see the source formats below for more detail on how to specify metas).}
]
As usual, you can use @racket[require], @racket[local-require], or @racket[dynamic-require] to retrieve these values. But within a Pollen project, the faster way is to use @racket[cached-doc] and @racket[cached-metas].
Pollen source files also make the @racket[metas] hashtable available through a submodule, also called @racket[metas]. So rather than importing a source file with @racket[(require "source.html.pm")], you would @racket[(require (submod "source.html.pm" metas))]. Accessing the metas this way avoids fully compiling the source file, and thus will usually be faster.
Pollen source files also make the @id{metas} hashtable available through a submodule, unsurprisingly called @id{metas}. So rather than importing a source file with @racket[(require "source.html.pm")], you can @racket[(require (submod "source.html.pm" metas))]. Accessing the metas this way avoids fully compiling the source file, and thus will usually be faster.
The names @racket[doc] and @racket[metas] can be changed for a project by overriding @racket[default-main-export] and @racket[default-meta-export].
The names @@id{doc} and @@id{metas} can be changed for a project by overriding @racket[default-main-export] and @racket[default-meta-export].
@margin-note{The Pollen rendering system relies on these two identifiers, but otherwise doesn't care how they're generated. Meaning, the code inside your Pollen source file could be @tt{#lang racket} or @tt{#lang whatever}. As long as you manually @racket[provide] those two identifiers and follow the usual file-naming convention, your source file will be usable.}
@margin-note{The Pollen rendering system relies on these two identifiers, but otherwise doesn't care how they're generated. Meaning, the code inside your Pollen source file could be @tt{#lang racket} or @tt{#lang whatever}. As long as you manually @racket[provide] those two identifiers and follow the usual file-naming convention, your source file will be renderable.}
@bold{How is this different from Racket?} In Racket, you must explicitly @racket[define] and then @racket[provide] any values you want to export.
@ -86,7 +92,7 @@ _...source...
Of course, you're better off specifying the preprocessor dialect explicitly rather than relying on this default behavior.
The output of the preprocessor dialect, provided by @racket['doc], is plain text.
The output of the preprocessor dialect, provided by @id{doc}, is plain text. For this reason, the preprocessor will convert everything it finds to text in the least surprising way possible.
@ -157,7 +163,9 @@ Scribble files are recognized by the project server and can be compiled and prev
Files with the null extension are simply rendered as a copy of the file without the extension, so @filepath{index.html.p} becomes @filepath{index.html}.
This can be useful you're managing your project with git. Most likely you'll want to ignore @filepath{*.html} and other file types that are frequently regenerated by the project server. But if you have isolated static files — for instance, a @filepath{index.html} that doesn't have source associated with it — they'll be ignored too. You can cure this problem by appending the null extension to these static files, so they'll be tracked in your source system without actually being source files.
This can be useful you're managing your project with Git. Most likely you'll want to ignore @filepath{*.html} and other file types that are frequently regenerated by the project server. But if you have isolated static files — for instance, a @filepath{index.html} that doesn't have source associated with it — they'll be ignored too. You can cure this problem by appending the null extension to these static files, so they'll be tracked in your source system without actually being source files.
The null extension is also useful for templates — @filepath{template.html} and @filepath{template.html.p} will work the same way.
@ -165,8 +173,8 @@ This can be useful you're managing your project with git. Most likely you'll wan
Pollen relies extensively on the convention of naming source files by adding a source extension to an output-file name. So the Pollen markup source for @filepath{index.html} would be @filepath{index.html.pm}.
This convention occasionally flummoxes other programs that assume a file can only have one extension. If you run into such a situation, you can @italic{escape} the output-file extension using the @racket[default-extension-escape-char], which defaults to the underscore @litchar{_}.
This convention occasionally flummoxes other programs that assume a file can only have one extension. If you run into such a situation, you can @italic{escape} the output-file extension with the underscore character @litchar{_}.
So instead of @filepath{index.html.pm}, your source-file name would be @filepath{index_html.pm}. When this source file is rendered, it will automatically be converted into @filepath{index.html} (meaning, the escaped extension will be converted into a normal file extension).
This alternative-naming scheme is automatically enabled in every project. You can also set the escape character on a per-project basis (see @racket[setup:extension-escape-char]). Pollen will let you choose any character, but of course it would be unwise to pick one with special meaning in your filesystem (for instance, @litchar{/}).
This alternative-naming scheme is automatically enabled in every project. You can also set the escape character on a per-project basis (by overriding @racket[defaul-extension-escape-char]). Pollen will let you choose any character, but of course it would be unwise to pick one with special meaning in your filesystem (for instance, @litchar{/}).

@ -14,7 +14,7 @@ Pollen will run on OS X, Linux, or Windows.
Pollen is not a self-contained GUI program like Adobe InDesign. It's a software package that runs atop the Racket language environment (also a free download).
Your three main tools in Pollen will be a text editor (for those starting out, I recommend @other-doc['(lib "scribblings/drracket/drracket.scrbl")]), a terminal window, and a web browser. The terminal commands you'll be using are simple, but if you haven't used your terminal window before, this is the moment to learn where it is.
Your three main tools in Pollen will be a text editor (for those starting out, I recommend @other-doc['(lib "scribblings/drracket/drracket.scrbl")]), a terminal window, and a web browser. The terminal commands you'll be using are simple, but if you haven't used your terminal window before, this is the moment to learn where it is. (On OS X, your terminal window is called Terminal; on Windows it's called the Windows Command Processor.)
After the initial download, Pollen does not require a network connection.
@ -37,24 +37,37 @@ Welcome to Racket v.@(version).
Type @exec{ctrl+D} to exit.
}
But if you get an error like this:
@terminal{Unrecognized command: racket}
@item{Windows users, when you see instructions that reference @exec{raco} — like the one below — I'll trust you to convert into the appropriate command for your system — assuming defaults, it's likely to be @filepath{C:\Program Files\Racket\raco} (include the surrounding quotes in the command).}
You have a deeper problem with your Racket installation that needs adjustment before continuing (usually a misconfiguration of @code{PATH}).
}
@item{Then, from the command line, install Pollen:
@commandline{raco pkg install pollen}
To check that it worked, try typing @exec{raco pollen version} on the command line, and you should see something like this:
To check that it worked, try typing @exec{raco pollen test} on the command line, and you should see this:
@terminal{~ : raco pollen version
@|pollen:version|
@terminal{~ : raco pollen test
raco pollen is installed correctly
~ :
}
But if you get:
@terminal{raco: Unrecognized command: pollen}
You'll need to fix the problem before proceeding, most likely by reinstalling Pollen.
@margin-note{Windows users, when you see instructions that reference @exec{raco}, I'll trust you to convert into the appropriate command for your system. Assuming defaults, it's likely to be @filepath{C:\Program Files\Racket\raco} (include the surrounding quotes in the command).}
}
@item{Alternatively, you can install Pollen from inside DrRacket with the @onscreen{File > Install Package ...} command.}
@item{Alternatively, you can install Pollen from inside DrRacket with the @menuitem["File" "Install Package ..."] command.}
@item{Either way, Pollen's documentation will be automatically installed.}

@ -47,7 +47,7 @@
(define (noskip-note)
(nested #:style (style "noskip" (list (css-style-addition mb-css) (alt-tag "div")))
(margin-note "Dont skip this section! It explains a concept that's essential to understanding how Pollen works.")))
(margin-note "Dont skip this section! It explains an essential Pollen concept.")))
(define-syntax (image/rp stx)

@ -1,13 +1,15 @@
/* special styles for custom @fileblock function in mb-tools.rkt */
.noskip .refcolumn {
background: #998;
background: #666;
border-color: #333;
color: white;
padding-top: 0.6rem;
}
.noskip .refcontent p {
font-size: 110%;
font-weight: bolder;
line-height: 1.4;
}

@ -199,7 +199,7 @@ six
@section{The automatic pagetree}
In situations where Pollen needs a pagetree but can't find one, it will automatically synthesize a pagetree from a listing of files in the directory. This arises most frequently when @secref["Using_the_dashboard" #:doc '(lib "pollen/scribblings/pollen.scrbl")] in a directory that doesn't contain an explicit @filepath{index.ptree}. This way, you can get going with a project without having to stop for @racketfont{.ptree} housekeeping.
In situations where Pollen needs a pagetree but can't find one, it will automatically synthesize a pagetree from a listing of files in the directory. This arises most frequently when @secref["The_project_dashboard"] in a directory that doesn't contain an explicit @filepath{index.ptree}. This way, you can get going with a project without having to stop for @racketfont{.ptree} housekeeping.
As usual, convenience has a cost. Pollen doesn't know anything about which files in your directory are relevant to the project, so it includes all of them. For instance, if you start your project server on an OS X desktop, you'll see things like @filepath{Thumbs.db} and @filepath{desktop.ini}.
@ -212,7 +212,7 @@ Typically you'll call the pagetree-navigation functions from inside templates, u
@section{Using @filepath{index.ptree} in the dashboard}
When you're using the project server to view the files in a directory, the server will first look for a file called @filepath{index.ptree}. If it finds this pagetree file, it will use it to build the dashboard. If not, then it will synthesize a pagetree using a directory listing. For more on this technique, see @secref["Using_the_dashboard" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
When you're using the project server to view the files in a directory, the server will first look for a file called @filepath{index.ptree}. If it finds this pagetree file, it will use it to build the dashboard. If not, then it will synthesize a pagetree using a directory listing. For more on this technique, see @secref["The_project_dashboard"].
@section{Using pagetrees with @exec{raco pollen render}}

@ -24,25 +24,25 @@ The @seclink["third-tutorial"]{third tutorial} introduced you to the idea that @
@item{Every Pollen tag calls a function with the same name.}
@item{The input values for that function are the attributes and content of the tag.}
@item{The input values for that function are the attributes and elements of the tag.}
@item{The whole tag — tag name, attributes, and content — is replaced with the return value of the called function.}
@item{The whole tag — tag name, attributes, and elements — is replaced with the return value of the called function.}
]
@subsection{Tag-function syntax}
To be safe, let's review what we mean by @italic{tag}, @italic{attributes}, and @italic{content}, in Pollen-mode syntax:
To be safe, let's review what we mean by @italic{tag}, @italic{attributes}, and @italic{elements}, in Pollen-mode syntax:
@codeblock[#:keep-lang-line? #f]{
#lang pollen
A tag alone: ◊get-author-name[]
A tag with content: Adding ◊em{emphasis to words}.
A tag with elements: Adding ◊em{emphasis to words}.
A tag with attributes and content:
A tag with attributes and elements:
◊div['((attr1 "val1")(attr2 "val2"))]{My nice div.}
A tag with attributes and content (alternate syntax):
A tag with attributes and elements (alternate syntax):
◊div[#:attr1 "val1" #:attr2 "val2"]{My nice div.}
}
@ -52,13 +52,13 @@ Let's also recall that these commands can be written in Racket mode equivalently
#lang pollen
A tag alone: ◊(get-author-name)
A tag with content: Adding ◊(em "emphasis to words").
A tag with elements: Adding ◊(em "emphasis to words").
A tag with attributes and content:
A tag with attributes and elements:
◊(div '((attr1 "val1")(attr2 "val2")) "My nice div.")
}
Let's also remember that a tag without attributes or content is interpreted as a value. If that's what you want — for instance, when you @racket[define] a tag to hold a value — then great. But if you @racket[define] a tag as a function, you need to add square brackets to signal that you want to evaluate the function:
Let's also remember that a tag without attributes or elements is interpreted as a value. If that's what you want — for instance, when you @racket[define] a tag to hold a value — then great. But if you @racket[define] a tag as a function, you need to add square brackets to signal that you want to evaluate the function:
@codeblock[#:keep-lang-line? #f]{
#lang pollen

@ -189,7 +189,7 @@ Suppose you want to change the inset to 30%. Without a preprocessor, you'd have
The @code{◊} character is called a @italic{lozenge}. In Pollen, the lozenge is a special character used to denote anything that Pollen should interpret as a command (rather than plain text).
If you're using DrRacket, you can insert a lozenge by clicking the @onscreen{Insert command char ◊} button at the top of your source window. (If you're not using DrRacket, see @seclink["The_lozenge_glyph____"]{these instructions}.)
If you're using DrRacket, you can insert a lozenge by clicking the @onscreen{Insert command char ◊} button at the top of your source window. (If you're not using DrRacket, see @seclink["the-lozenge"]{these instructions}.)
Thus, the command @code{◊(define my-inset "30%")} means ``create the variable @code{my-inset} and assign it the value @racket{30%}.''
@ -276,7 +276,7 @@ But what if you wanted to use Pollen as a preprocessor that outputs a Markdown f
@section{Pollen markup}
If all you need to do is produce basic HTML, Markdown is fine. But if you need to do semantic markup or other kinds of custom markup, it's @seclink["The_case_against_Markdown"]{not flexible enough}.
If all you need to do is produce basic HTML, Markdown is fine. But if you need to do semantic markup or other kinds of custom markup, it's @seclink["the-case-against-markdown"]{not flexible enough}.
In that case, you can use a different authoring mode, called @defterm{Pollen markup}. To use Pollen markup, insert @code{#lang pollen} as the first line of your source as usual, but this time add a @filepath{.pm} file extension.
@ -381,7 +381,7 @@ Now you've seen the key features of Pollen. What do you think?
@item{@italic{``What about pairing a Python template system and Python web server?''} Good idea. I even tried it. But Python template systems don't offer you Python — they offer you limited dialects that aren't very Pythonic. Also, Python's handing of XML-ish data is cumbersome.}
@item{@italic{``Haven't you heard of Jekyll?''} Yes. If everything you need to write is expressible in Markdown, it's great. If you need more than that, you're stuck. (See also @seclink["The_case_against_Markdown"]{my objections to Markdown for books}.)}
@item{@italic{``Haven't you heard of Jekyll?''} Yes. If everything you need to write is expressible in Markdown, it's great. If you need more than that, you're stuck. (See also @seclink["the-case-against-markdown"]{my objections to Markdown for books}.)}
@item{@italic{``Sounds a lot like LaTeX. Why not use that?''} Also a good idea. LaTeX gets a lot of things right. But it's also missing a lot — for instance, Unicode and web publishing.}

@ -47,11 +47,11 @@ Displays a list of available commands.
@section{@exec{raco pollen start}}
Start the project server from the current directory using the default port, which is the value of the parameter @racket[current-server-port] (by default, port @(format "~a" default-project-server-port)).
Start the project server from the current directory using the default port, which is the value of the parameter @racket[current-server-port] (by default, port @id[default-project-server-port]).
This command can be invoked with two optional arguments.
@racket[raco pollen start _path] will start the project server in @racket[_path] rather than the current directory.
@racket[raco pollen start _path] will start the project server from @racket[_path] rather than the current directory (making @racket[_path] its root directory).
@terminal{
> raco pollen start ~/path/to/project/}
@ -107,7 +107,7 @@ Make a copy of the project directory on the desktop, but without any source file
If you're already in your project directory and want to publish somewhere other than the desktop, use @racket[raco pollen publish _. _dest-dir].
You can determine the files that get filtered out in a particular project by using @racket[setup:unpublished-path?].
You can determine the files that get filtered out in a particular project by overriding @racket[default-unpublished-path?].
@section{@exec{raco pollen setup}}
@ -151,3 +151,4 @@ Result is DEBUG
}

@ -1,10 +1,8 @@
#lang scribble/manual
@title{Backstory}
I created Pollen to overcome limitations & frustrations I repeatedly encountered with existing web-publishing tools.
I created Pollen to overcome limitations & frustrations I repeatedly encountered with existing web-publishing tools.
If you agree with my characterization of those problems, then you'll probably like the solution that Pollen offers. If not, then you probably won't.
@ -16,13 +14,13 @@ If you weren't around then, you didn't miss much. Everything about the web was h
That's no longer true. The web is now more than 20 years old. During that time, most parts of the web have improved dramatically — for instance, the connections are faster, the browsers are more sophisticated, and the screens have more pixels.
But one part hasn't improved much: the way we make web pages. Over the years, tools promising to simplify web development have come and mostly gone — from @link["http://www.macobserver.com/reviews/pagemill2.shtml"]{PageMill} to @link["http://www.adobe.com/products/dreamweaver.html"]{Dreamweaver} to @link["http://wordpress.org"]{WordPress} to @link["http://jekyllrb.com"]{Jekyll}. Meanwhile, true web jocks have remained loyal to the original HTML power tool: the humble text editor.
But one part hasn't improved much: the way we make web pages. Over the years, tools promising to simplify web development have come and mostly gone — from @link["http://www.macobserver.com/reviews/pagemill2.shtml"]{PageMill} to @link["http://www.adobe.com/products/dreamweaver.html"]{Dreamweaver} to @link["http://www.squarespace.com"]{Squarespace}. Meanwhile, serious web jocks have remained loyal to the original HTML power tool: the humble text editor.
In one way, this makes sense. Web pages are mostly made of text-based data — HTML, CSS, JavaScript, and so on — and the simplest way to mainpulate this data is with a text editor. While HTML and CSS are @link["http://programmers.stackexchange.com/questions/28098/why-does-it-matter-that-html-and-css-are-not-programming-languages"]{not} programming languages, they lend themselves to semantic and logical structure that's most easily expressed by editing them as text. Furthermore, text-based editing makes debugging and performance improvements easier.
In one way, this makes sense. Web pages are made mostly of text-based data — HTML, CSS, JavaScript, and so on — and the simplest way to mainpulate this data is with a text editor. While HTML and CSS are not programming languages — you can't even compute 1 + 1 — they lend themselves to semantic and logical structure that's most easily expressed by editing them as text. Furthermore, text-based editing makes debugging and performance improvements easier.
But text-based editing is also limited. Though the underlying description of a web page is notionally human-readable, it's optimized to be readable by other software — namely, web browsers. HTML in particular is verbose and easily mistyped. And isn't it fatally dull to manage all the boilerplate, like surrounding every paragraph with @code{<p>...</p>}? Yes, it is.
But text-based editing is also limited. Though the underlying description of a web page is notionally human-readable, it's optimized to be readable by other software — namely, web browsers. HTML in particular is verbose and easily mistyped. And isn't it fatally dull to manage all the boilerplate, like surrounding every paragraph with @code{<p>...</p>}? Yes, it is.
For these reasons, much of web development should lend itself to @italic{abstraction} & @italic{automation}. Abstraction means consolidating repetitive, complex patterns into simpler, parameterized forms. Automation means avoiding the manual drudgery of generating the output files. But in practice, tools that enable this abstraction & automation have been slow to arrive, and most have come hobbled with unacceptable deficiencies.
For these reasons, much of web development should've lent itself to @italic{abstraction} & @italic{automation}. Abstraction means consolidating repetitive, complex patterns into simpler, more generalized forms. Automation means avoiding the drudgery and potential for error inherent in generating output files by hand. In other words, it starts to look a lot like programming.
@section{The better idea: a programming model}
@ -36,7 +34,13 @@ On the early web, the text-editing model was appealingly precise and quick. On s
What followed was a steady stream of products, frameworks, tools, and content management systems that claimed to bring a programming model to web development. Some were better than others. But none of them displaced the text editor as the preferred tool of web developers. And none of them matched the power and flexibility you get from any reasonable programming language.
Why not? These tools always promised a great leap forward in solving the web-development problem. In practice, however, they simply redistributed the pain. I needn't bore you by enumerating the deficiencies of specific tools, because they've tended to fail in the thematic ways:
Why not? These tools always promised a great leap forward in solving the web-development problem. In practice, however, they simply redistributed the pain.
Today, for instance, it's possible to find multiple preprocessors for @link["http://haml.info"]{HTML}, @link["http://sass-lang.com"]{CSS}, and @link["http://coffeescript.org"]{JavaScript}. But they're all separate tools, with different syntax and functions. Good luck finding a single preprocessor that can handle all your web files simultaneously.
This kind of thinking — from the edges inward, rather than from the center out — has been the thematic failure of web-publishing tools. Each is like the blind man of proverb with a single @link["http://www.jainworld.com/literature/story25.htm"]{hand on the elephant} — addressing a specific concern while missing the broader context.
Likewise, even web-publishing systems ostensibly based on general-purpose programming languages — like @link["https://wordpress.org"]{WordPress} or @link["https://www.djangoproject.com"]{Django} — suffer from recurring deficiencies:
@itemlist[
@ -59,7 +63,7 @@ In 2008, I launched a website called @link["http://typographyforlawyers.com"]{@i
So I used @link["http://wordpress.org"]{WordPress}. The major chore became scraping out all the crap that typically lives in blog templates. It ended up looking simpler & cleaner than the usual WordPress website. Largely because of this, people @link["http://ma.tt/2010/04/typography-for-lawyers/"]{liked it}.
Eventually, a publisher offered to release it as @link["http://typo.la/amzn"]{a paperback}, which came out in 2010.
Eventually, a publisher offered to release it as @link["http://typo.la/amzn"]{a paperback}, which came out in 2010 (the second edition was released in 2015).
Later came the inevitable request to make it into a Kindle book. As a fan of typography, I hate the Kindle. The layout controls are coarse, and so is the reading experience. But I didn't run and hide. Basically a Kindle book is a little website made with 1995-era HTML. So I coded up some tools in Perl to convert my book to Kindle format while preserving the formatting and images as well as possible.
@ -75,7 +79,7 @@ Did it work? Sort of. Source code went in; web pages came out. But it was also c
I had come across Racket while researching languages suitable for HTML/XML processing. I had unexpectedly learned about the @link["http://www.defmacro.org/ramblings/lisp.html"]{secret kinship} of XML and Lisp: though XML is not a programming language, it uses a variant of Lisp syntax. Thus Lisp languages are particularly adept at handling XMLish structures. That was interesting.
After comparing some of the Lisp & Scheme variants, @link["http://practicaltypography.com/why-racket-why-lisp.html"]{Racket stood out} because it had a text-based dialect called Scribble. Scribble could be used to embed code within textual content. That was interesting too. Among other things, this meant Scribble could be used as a general-purpose preprocessor. So I thought I'd see if I could add it to Pollen.
After comparing some of the Lisp & Scheme variants, @link["http://practicaltypography.com/why-racket-why-lisp.html"]{Racket stood out} because it had a text-based dialect called @seclink["getting-started" #:doc '(lib "scribblings/scribble/scribble.scrbl")]{Scribble}. Scribble could be used to embed code within textual content. That was interesting too. Among other things, this meant Scribble could be used as a @seclink["text" #:doc '(lib "scribblings/scribble/scribble-pp.scrbl")]{general-purpose preprocessor}. So I thought I'd see if I could add it to Pollen.
It worked. So well, in fact, that I started thinking about whether I could reimplement other parts of Pollen in Racket. Then I started thinking about reimplementing all of it in Racket.
@ -83,7 +87,7 @@ So I did. And here we are.
@section{What is Pollen?}
Pollen is a publishing system built on top of Scribble and Racket. So far, I've optimized Pollen for digital books, because that's mainly what I use it for. But it can be used for small projects too.
Pollen is a publishing system built on top of Scribble and Racket. So far, I've optimized Pollen for web-based books, because that's mainly what I use it for. But it can be used for small projects too, and non-webby things like @seclink["Adding_support_for_PDF_output"]{PDF}.
As a publishing system, Pollen includes:
@ -102,7 +106,7 @@ Pollen addresses the deficiencies I experienced with other tools:
@itemlist[
@item{@bold{Yes, we have a native data structure for HTML.} Racket represents HTML structures as @secref["X-expressions" #:doc '(lib "pollen/scribblings/pollen.scrbl")], which are a variant of the standard Racket data structure, called @italic{S-expressions}. In other words, not only is there a native representation for HTML, but it's represented the same way as everything else in the language.}
@item{@bold{Yes, we have a native data structure for HTML.} Racket represents HTML structures as @secref["X-expressions"], which are a variant of the standard Racket data structure, called @italic{S-expressions}. In other words, not only is there a native representation for HTML, but it's represented the same way as everything else in the language.}
@item{@bold{Flexible blending of code, presentation, and content.} Pollen is a text-based language. So a Pollen source file might have no code at all. But as a dialect of Scribble & Racket, if you want to mix code with content, you can.}

@ -1,6 +1,6 @@
#lang scribble/manual
@(require (for-label pollen/setup racket) "mb-tools.rkt")
@(require (for-label pollen/setup racket) "mb-tools.rkt" pollen/private/version)
@title[#:tag "first-tutorial"]{First tutorial: the project server & preprocessor}
@ -8,9 +8,9 @@ In this tutorial, you'll use Pollen to make a single HTML page with a poem. You'
@itemlist[
@item{The relationship of Racket & Pollen}
@item{The relationship of Pollen & Racket}
@item{DrRacket}
@item{DrRacket, the code editor}
@item{The project server}
@ -22,11 +22,11 @@ If you want the shortest possible introduction to Pollen, try the @secref["quick
@section[#:tag-prefix "tutorial-1"]{Prerequisites}
I'm going to assume that you've already installed Racket and Pollen. If not, do that now.
I'm going to assume that you've already @seclink["Installation"]{installed} Racket and Pollen. If not, do that now.
I'm also going to assume you know the basics of using a command line to run programs and navigate the file system using commands like @exec{cd} and @exec{ls}. On Mac OS X, your command-line program is called Terminal; on Windows it's the Windows Command Processor.
I'm also going to assume you know the basics of using a command line to run programs and navigate your file system using commands like @exec{cd} and @exec{ls}.
@section{The relationship of Racket & Pollen}
@section{Optional reading: the relationship of Racket & Pollen}
As I mentioned in the @secref["big-picture"], Pollen is built using Racket, and everything in Pollen ultimately becomes Racket code. If you're comfortable with that idea, you may move along.
@ -34,17 +34,15 @@ But if not, or if you're just a curious character:
One of the key features of Racket as a programming language is that it provides tools to create @italic{other} programming languages. These languages might look & behave @link["http://docs.racket-lang.org/ts-guide/index.html"]{like Racket}. Or they @link["http://hashcollision.org/brainfudge/"]{might not}. These languages might serve a general purpose, but more often they're specialized for a particular purpose, in which case they're known as @italic{domain-specific languages}, or @italic{DSLs}.
@margin-note{Racket exploits the fact that under the hood, all programming languages are basically doing the same thing. (CS jocks know this more formally as a side effect of @link["https://en.wikipedia.org/wiki/Turing_completeness"]{Turing completeness}.) Racket starts with the most general expression of a Turing-complete language — called @link["https://en.wikipedia.org/wiki/Lambda_calculus"]{the lambda calculus} — and lets users build on that. In most programming languages, you can build functions, classes, and modules. But in Racket, you can alter anything about the language.}
If you find this a strange idea, you're not alone. Most programmers — and until recently, me too — have never made or used DSLs. If you have a programming problem to solve, you start with a general-purpose language like Python or Java or Ruby, and go from there. Nothing wrong with that.
But programming languages contain their own design choices and compromises. Sometimes the problem at hand is best solved by manipulating the language at a deeper level. When you make a DSL, you're still programming in the underlying language, but doing so at a point of higher leverage.
Pollen is a DSL implemented in Racket. It is a close cousin of @other-doc['(lib "scribblings/scribble/scribble.scrbl")], another Racket DSL, which was designed for writing Racket documentation. The key feature of Scribble, and thus also of Pollen, is that it's text-based. Meaning, whereas most languages have source files made of code with text embedded within, Pollen's source files are text with code embedded within.
Moreover, Pollen is meant to be a small step away from Racket — you can think of it as a more convenient notation system for Racket code, similar to how Markdown is a more convenient notation for HTML. But unlike Markdown, which only lets you access a subset of HTML, anything that can be done in Racket can also be done in Pollen.
Moreover, Pollen is meant to be a small step away from Racket — you can think of it as a more convenient notation system for Racket code, similar to how Markdown is a more convenient notation for HTML. But unlike Markdown, which only lets you access a subset of HTML, anything that can be done in Racket can also be done in Pollen. (Also unlike Markdown, Pollen and Racket are full programming languages.)
As you work more with Pollen, you'll pick up more about how Pollen corresponds to Racket (see @secref["pollen-command-syntax"]) and easily be able to convert commands from one system to the other. In later tutorials, you'll see how larger Pollen projects are made out of both Pollen and Racket source files.
As you work with Pollen, you'll become familiar with how Pollen @seclink["pollen-command-syntax"]{corresponds to Racket}, and easily be able to convert commands from one notation to the other. In later tutorials, you'll see how larger Pollen projects can be assembled out of a mix of Pollen and Racket source files.
But in smaller projects, like this one, you can just use Pollen.
@ -52,7 +50,7 @@ But in smaller projects, like this one, you can just use Pollen.
DrRacket is the IDE for the Racket programming language, and other languages made with Racket (like Pollen). IDE stands for ``Integrated Development Environment,'' which is a fancy phrase for ``a nice place to edit and run your code.'' DrRacket is installed as part of the core Racket distribution.
@margin-note{If you've worked with languages like Perl, Python, or Ruby, you may be more familiar with using a general-purpose text editor to edit your code, and then running your program at the command line. You can do that with Racket too. But DrRacket is a considerately designed tool. I recommend it. For these tutorials, I'll assume you're using DrRacket. If you insist on using the command line, I trust you to figure out what you need to do to keep up.}
@margin-note{Experienced programmers may prefer to use a general-purpose text editor and the command line to edit and run Racket programs. That's fine. But for these tutorials, I'll assume you're using DrRacket. Otherwise, I trust you to figure out what you need to do to keep up.}
Launch DrRacket. Start a new file. The code in the file will look like this:
@ -63,7 +61,7 @@ Launch DrRacket. Start a new file. The code in the file will look like this:
Within the main window, you should also see an @defterm{interactions window}, which shows the output of the current file, and starts out looking something like this (details, like the version number, will vary):
@terminal{
Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].
Welcome to DrRacket, version @(version)--2015-11-26(-/f) [3m].
Language: racket; memory limit: 1000 MB.
> }
@ -85,7 +83,7 @@ When you start a new Pollen source file in DrRacket, you'll need to change the @
Now run your file by clicking the @onscreen["Run"] button in the upper-right corner, or select @menuitem["Racket" "Run"] from the menu. You'll get something like:
@terminal{
Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].
Welcome to DrRacket, version @(version)--2015-11-26(-/f) [3m].
Language: pollen; memory limit: 1000 MB.
>
}
@ -139,9 +137,16 @@ This shows you something important: by default, any plain text in a Pollen sourc
File naming in Pollen is consequential.
Ultimately, every Pollen source file in your project will be @defterm{rendered} into an output file. Each Pollen source file corresponds to one output file. @bold{The