diff --git a/doc/Pagetree.html b/doc/Pagetree.html index 89f4383..06178d9 100644 --- a/doc/Pagetree.html +++ b/doc/Pagetree.html @@ -1,2 +1,2 @@ -11.4 Pagetree
11 Module reference
11.1 Cache
11.2 Decode
11.3 File
11.4 Pagetree
11.5 Render
11.6 Template
11.7 Tag
11.8 Top
11.9 World
On this page:
11.4.1 Making pagetrees with a source file
11.4.2 Making pagetrees by hand
11.4.3 Using pagetrees for navigation
11.4.4 Using index.ptree in the dashboard
11.4.5 Using pagetrees with raco pollen render
11.4.6 Functions
11.4.6.1 Predicates & validation
pagetree?
validate-pagetree
pagenode?
pagenodeish?
->pagenode
11.4.6.2 Navigation
current-pagetree
parent
children
siblings
previous
previous*
next
next*
11.4.6.3 Utilities
pagetree->list
in-pagetree?
path->pagenode
6.1.0.5

11.4 Pagetree

 (require pollen/pagetree) package: pollen

Books and other long documents are usually organized in a structured way — at minimum they have a sequence of pages, but more often they have sections with subsequences within. Individual pages in a Pollen project don’t know anything about how they’re connected to other pages. In theory, you could maintain this information within the source files. But this would be a poor use of human energy.

Instead, use a pagetree. A pagetree is a simple abstraction for defining & working with sequences of pagenodes. Typically these pagenodes will be the names of output files in your project.

“So it’s a list of web-page filenames?” Sort of. When I think of a web page, I think of an actual file on a disk. Keeping with Pollen’s orientation toward dynamic rendering, pagenodes may — and often do — refer to files that don’t yet exist. Moreover, by referring to output names rather than source names, you retain the flexibility to change the kind of source associated with a particular pagenode (e.g., from preprocessor source to Pollen markup).

Pagetrees can be flat or hierarchical. A flat pagetree is just a list of pagenodes. A hierarchical pagetree can also contain recursively nested lists of pagenodes. But you needn’t pay attention to this distinction, as the pagetree functions don’t care which kind you use. Neither do I.

Pagetrees surface throughout the Pollen system. They’re primarily used for navigation — for instance, calculating “previous,” “next,” or “up” links for a given page. A special pagetree, index.ptree, is used by the project server to order the files in a dashboard. Pagetrees can also be used to define batches of files for certain operations, for instance raco pollen render. You might find other uses for them too.

11.4.1 Making pagetrees with a source file

A pagetree source file either starts with #lang pollen and uses the .ptree extension, or starts with #lang pollen/ptree and then can have any file extension.

Unlike other Pollen source files, since the pagetree source is not rendered into an output format, the rest of the filename is up to you.

Here’s a flat pagetree. Each line is considered a single pagenode (blank lines are ignored). Notice that no Pollen command syntax nor quoting is needed within the pagetree source:

"flat.ptree"
#lang pollen
 
index.html
introduction.html
main_argument.html
conclusion.html

And here’s the output in DrRacket:

'(pagetree-root index.html introduction.html main_argument.html conclusion.html)

Keeping with usual Pollen policy, this is an X-expression. The pagetree-root is just an arbitrary tag that contains the pagetree.

Upgrading to a hierarchical pagetree is simple. The same basic rule applies — one pagenode per line. But this time, you add Pollen command syntax: a lozenge in front of a pagenode marks it as the top of a nested list, and the sub-pagenodes of that list go between { curly braces }, like so:

"hierarchical.ptree"
#lang pollen
 
toc.html
first-chapter.html{
    foreword.html
    introduction.html}
second-chapter.html{
    main-argument.html{
        facts.html
        analysis.html}
    conclusion.html}
bibliography.html

The output of our hierarchical pagetree:

'(pagetree-root toc.html (first-chapter.html foreword.html introduction.html) (second-chapter.html (main-argument.html facts.html analysis.html) conclusion.html) bibliography.html)

One advantage of using a source file is that when you run it in DrRacket, it will automatically be checked using validate-pagetree, which insures that every element in the pagetree meets pagenode?, and that all the pagenodes are unique.

This pagetree has a duplicate pagenode, so it won’t run:

"duplicate-pagenode.ptree"
#lang pollen
 
index.html
introduction.html
main_argument.html
conclusion.html
index.html

Instead, you’ll get an error:

validate-pagetree: members-unique? failed because item isn’t unique: (index.html)

11.4.2 Making pagetrees by hand

Experienced programmers may want to know that because a pagetree is just an X-expression, you can synthesize a pagetree using any Pollen or Racket tools for making X-expressions. For example, here’s some Racket code that generates the same pagetree as the flat.ptree source file above:

"make-flat-ptree.rkt"
#lang racket
(require pollen/pagetree)
(define node-names '(index introduction main_argument conclusion))
(define pt `(pagetree-root
  ,@(map (λ(n) (string->symbol (format "~a.html" n))) node-names)))
(if (pagetree? pt) pt "Oops, not a pagetree")

Note that you need to take more care when building a pagetree by hand. Pagenodes are symbols, not strings, thus the use of string->symbol is mandatory. One benefit of using a pagetree source file is that it takes care of this housekeeping for you.

11.4.3 Using pagetrees for navigation

Typically you’ll call the pagetree-navigation functions from inside templates, using the special variable here as the starting point. For more on this technique, see Pagetree navigation.

11.4.4 Using 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 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 Using the dashboard.

11.4.5 Using pagetrees with raco pollen render

The raco pollen render command is used to regenerate an output file from its source. If you pass a pagetree to raco pollen render, it will automatically render each file listed in the pagetree.

For instance, many projects have auxiliary pages that don’t really belong in the main navigational flow. You can collect these pages in a separate pagetree:

"utility.ptree"
#lang pollen
 
404-error.html
terms-of-service.html
webmaster.html
[... and so on]

Thus, when you’re using pagetree-navigation functions within a template, you can use your main pagetree, and restrict the navigation to the main editorial content. But when you render the project, you can pass both pagetrees to raco pollen render.

For more on this technique, see raco pollen render.

11.4.6 Functions
11.4.6.1 Predicates & validation

procedure

(pagetree? possible-pagetree)  boolean?

  possible-pagetree : any/c
Test whether possible-pagetree is a valid pagetree. It must be a txexpr? where all elements are pagenode?, and each is unique within possible-pagetree (not counting the root node).

Examples:

> (pagetree? '(root index.html))

#t

> (pagetree? '(root duplicate.html duplicate.html))

#f

> (pagetree? '(root index.html "string.html"))

#f

> (define nested-ptree '(root 1.html 2.html (3.html 3a.html 3b.html)))
> (pagetree? nested-ptree)

#t

> (pagetree? `(root index.html ,nested-ptree (subsection.html more.html)))

#t

; Nesting a subtree twice creates duplication
> (pagetree? `(root index.html ,nested-ptree (subsection.html ,nested-ptree)))

#f

procedure

(validate-pagetree possible-pagetree)  pagetree?

  possible-pagetree : any/c
Like pagetree?, but raises a descriptive error if possible-pagetree is invalid, and otherwise returns possible-pagetree itself.

Examples:

> (validate-pagetree '(root (mama.html son.html daughter.html) uncle.html))

'(root (mama.html son.html daughter.html) uncle.html)

> (validate-pagetree `(root (,+ son.html daughter.html) uncle.html))

#f

> (validate-pagetree '(root (mama.html son.html son.html) mama.html))

validate-pagetree: members-unique? failed because items

aren’t unique: (son.html mama.html)

procedure

(pagenode? possible-pagenode)  boolean?

  possible-pagenode : any/c
Test whether possible-pagenode is a valid pagenode. A pagenode can be any symbol? that is not whitespace/nbsp? Every leaf of a pagetree is a pagenode. In practice, your pagenodes will likely be names of output files.

Pagenodes are symbols (rather than strings) so that pagetrees will be valid tagged X-expressions, which is a more convenient format for validation & processing.

Examples:

; Three symbols, the third one annoying but valid
> (map pagenode? '(symbol index.html |   silly   |))

'(#t #t #t)

; A number, a string, a txexpr, and a whitespace symbol
> (map pagenode? '(9.999 "index.html" (p "Hello") |    |))

'(#f #f #f #f)

procedure

(pagenodeish? v)  boolean?

  v : any/c
Return #t if v can be converted with ->pagenode.

Example:

> (map pagenodeish? '(9.999 "index.html" |    |))

'(#t #t #f)

procedure

(->pagenode v)  pagenode?

  v : pagenodeish?
Convert v to a pagenode.

Examples:

> (map pagenodeish? '(symbol 9.999 "index.html" |  silly  |))

'(#t #t #t #t)

> (map ->pagenode '(symbol 9.999 "index.html" |  silly  |))

'(symbol |9.999| index.html |  silly  |)

11.4.6.2 Navigation

parameter

(current-pagetree)  pagetree?

(current-pagetree pagetree)  void?
  pagetree : pagetree?
A parameter that defines the default pagetree used by pagetree navigation functions (e.g., parent-pagenode, chidren, et al.) if another is not explicitly specified. Initialized to #f.

procedure

(parent p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the parent pagenode of p within pagetree. Return #f if there isn’t one.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (parent 'son.html)

'mama.html

> (parent "mama.html")

'root

> (parent (parent 'son.html))

'root

> (parent (parent (parent 'son.html)))

#f

procedure

(children p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the child pagenodes of p within pagetree. Return #f if there aren’t any.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (children 'mama.html)

'(son.html daughter.html)

> (children 'uncle.html)

#f

> (children 'root)

'(mama.html uncle.html)

> (map children (children 'root))

'((son.html daughter.html) #f)

procedure

(siblings p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the sibling pagenodes of p within pagetree. The list will include p itself. But the function will still return #f if pagetree is #f.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (siblings 'son.html)

'(son.html daughter.html)

> (siblings 'daughter.html)

'(son.html daughter.html)

> (siblings 'mama.html)

'(mama.html uncle.html)

procedure

(previous p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)

procedure

(previous* p [pagetree])  (or/c #f (listof pagenode?))

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Return the pagenode immediately before p. For previous*, return all the pagenodes before p, in sequence. In both cases, return #f if there aren’t any pagenodes. The root pagenode is ignored.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (previous 'daughter.html)

'son.html

> (previous 'son.html)

'mama.html

> (previous (previous 'daughter.html))

'mama.html

> (previous 'mama.html)

#f

> (previous* 'daughter.html)

'(mama.html son.html)

> (previous* 'uncle.html)

'(mama.html son.html daughter.html)

procedure

(next p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)

procedure

(next* p [pagetree])  (or/c #f (listof pagenode?))

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Return the pagenode immediately after p. For next*, return all the pagenodes after p, in sequence. In both cases, return #f if there aren’t any pagenodes. The root pagenode is ignored.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (next 'son.html)

'daughter.html

> (next 'daughter.html)

'uncle.html

> (next (next 'son.html))

'uncle.html

> (next 'uncle.html)

#f

> (next* 'mama.html)

'(son.html daughter.html uncle.html)

> (next* 'daughter.html)

'(uncle.html)

11.4.6.3 Utilities

procedure

(pagetree->list pagetree)  list?

  pagetree : pagetree?
Convert pagetree to a simple list. Equivalent to a pre-order depth-first traversal of pagetree.

procedure

(in-pagetree? pagenode [pagetree])  boolean?

  pagenode : pagenode?
  pagetree : pagetree? = (current-pagetree)
Report whether pagenode is in pagetree.

procedure

(path->pagenode p)  pagenode?

  p : pathish?
Convert path p to a pagenode — meaning, make it relative to current-project-root, run it through ->output-path, and convert it to a symbol. Does not tell you whether the resultant pagenode actually exists in the current pagetree (for that, use in-pagetree?).

 
\ No newline at end of file +11.4 Pagetree
11 Module reference
11.1 Cache
11.2 Decode
11.3 File
11.4 Pagetree
11.5 Render
11.6 Template
11.7 Tag
11.8 Top
11.9 World
On this page:
11.4.1 Making pagetrees with a source file
11.4.2 Making pagetrees by hand
11.4.3 Using pagetrees for navigation
11.4.4 Using index.ptree in the dashboard
11.4.5 Using pagetrees with raco pollen render
11.4.6 Functions
11.4.6.1 Predicates & validation
pagetree?
validate-pagetree
pagenode?
pagenodeish?
->pagenode
11.4.6.2 Navigation
current-pagetree
parent
children
siblings
previous
previous*
next
next*
11.4.6.3 Utilities
pagetree->list
in-pagetree?
path->pagenode
6.1.0.5

11.4 Pagetree

 (require pollen/pagetree) package: pollen

Books and other long documents are usually organized in a structured way — at minimum they have a sequence of pages, but more often they have sections with subsequences within. Individual pages in a Pollen project don’t know anything about how they’re connected to other pages. In theory, you could maintain this information within the source files. But this would be a poor use of human energy.

Instead, use a pagetree. A pagetree is a simple abstraction for defining & working with sequences of pagenodes. Typically these pagenodes will be the names of output files in your project.

“So it’s a list of web-page filenames?” Sort of. When I think of a web page, I think of an actual file on a disk. Keeping with Pollen’s orientation toward dynamic rendering, pagenodes may — and often do — refer to files that don’t yet exist. Moreover, by referring to output names rather than source names, you retain the flexibility to change the kind of source associated with a particular pagenode (e.g., from preprocessor source to Pollen markup).

Pagetrees can be flat or hierarchical. A flat pagetree is just a list of pagenodes. A hierarchical pagetree can also contain recursively nested lists of pagenodes. But you needn’t pay attention to this distinction, as the pagetree functions don’t care which kind you use. Neither do I.

Pagetrees surface throughout the Pollen system. They’re primarily used for navigation — for instance, calculating “previous,” “next,” or “up” links for a given page. A special pagetree, index.ptree, is used by the project server to order the files in a dashboard. Pagetrees can also be used to define batches of files for certain operations, for instance raco pollen render. You might find other uses for them too.

11.4.1 Making pagetrees with a source file

A pagetree source file either starts with #lang pollen and uses the .ptree extension, or starts with #lang pollen/ptree and then can have any file extension.

Unlike other Pollen source files, since the pagetree source is not rendered into an output format, the rest of the filename is up to you.

Here’s a flat pagetree. Each line is considered a single pagenode (blank lines are ignored). Notice that no Pollen command syntax nor quoting is needed within the pagetree source:

"flat.ptree"
#lang pollen
 
index.html
introduction.html
main_argument.html
conclusion.html

And here’s the output in DrRacket:

'(pagetree-root index.html introduction.html main_argument.html conclusion.html)

Keeping with usual Pollen policy, this is an X-expression. The pagetree-root is just an arbitrary tag that contains the pagetree.

Upgrading to a hierarchical pagetree is simple. The same basic rule applies — one pagenode per line. But this time, you add Pollen command syntax: a lozenge in front of a pagenode marks it as the top of a nested list, and the sub-pagenodes of that list go between { curly braces }, like so:

"hierarchical.ptree"
#lang pollen
 
toc.html
first-chapter.html{
    foreword.html
    introduction.html}
second-chapter.html{
    main-argument.html{
        facts.html
        analysis.html}
    conclusion.html}
bibliography.html

The output of our hierarchical pagetree:

'(pagetree-root toc.html (first-chapter.html foreword.html introduction.html) (second-chapter.html (main-argument.html facts.html analysis.html) conclusion.html) bibliography.html)

One advantage of using a source file is that when you run it in DrRacket, it will automatically be checked using validate-pagetree, which insures that every element in the pagetree meets pagenode?, and that all the pagenodes are unique.

This pagetree has a duplicate pagenode, so it won’t run:

"duplicate-pagenode.ptree"
#lang pollen
 
index.html
introduction.html
main_argument.html
conclusion.html
index.html

Instead, you’ll get an error:

validate-pagetree: members-unique? failed because item isn’t unique: (index.html)

11.4.2 Making pagetrees by hand

Experienced programmers may want to know that because a pagetree is just an X-expression, you can synthesize a pagetree using any Pollen or Racket tools for making X-expressions. For example, here’s some Racket code that generates the same pagetree as the flat.ptree source file above:

"make-flat-ptree.rkt"
#lang racket
(require pollen/pagetree)
(define node-names '(index introduction main_argument conclusion))
(define pt `(pagetree-root
  ,@(map (λ(n) (string->symbol (format "~a.html" n))) node-names)))
(if (pagetree? pt) pt "Oops, not a pagetree")

Note that you need to take more care when building a pagetree by hand. Pagenodes are symbols, not strings, thus the use of string->symbol is mandatory. One benefit of using a pagetree source file is that it takes care of this housekeeping for you.

11.4.3 Using pagetrees for navigation

Typically you’ll call the pagetree-navigation functions from inside templates, using the special variable here as the starting point. For more on this technique, see Pagetree navigation.

11.4.4 Using 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 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 Using the dashboard.

11.4.5 Using pagetrees with raco pollen render

The raco pollen render command is used to regenerate an output file from its source. If you pass a pagetree to raco pollen render, it will automatically render each file listed in the pagetree.

For instance, many projects have auxiliary pages that don’t really belong in the main navigational flow. You can collect these pages in a separate pagetree:

"utility.ptree"
#lang pollen
 
404-error.html
terms-of-service.html
webmaster.html
[... and so on]

Thus, when you’re using pagetree-navigation functions within a template, you can use your main pagetree, and restrict the navigation to the main editorial content. But when you render the project, you can pass both pagetrees to raco pollen render.

For more on this technique, see raco pollen render.

11.4.6 Functions
11.4.6.1 Predicates & validation

procedure

(pagetree? possible-pagetree)  boolean?

  possible-pagetree : any/c
Test whether possible-pagetree is a valid pagetree. It must be a txexpr? where all elements are pagenode?, and each is unique within possible-pagetree (not counting the root node).

Examples:

> (pagetree? '(root index.html))

#t

> (pagetree? '(root duplicate.html duplicate.html))

#f

> (pagetree? '(root index.html "string.html"))

#f

> (define nested-ptree '(root 1.html 2.html (3.html 3a.html 3b.html)))
> (pagetree? nested-ptree)

#t

> (pagetree? `(root index.html ,nested-ptree (subsection.html more.html)))

#t

; Nesting a subtree twice creates duplication
> (pagetree? `(root index.html ,nested-ptree (subsection.html ,nested-ptree)))

#f

procedure

(validate-pagetree possible-pagetree)  pagetree?

  possible-pagetree : any/c
Like pagetree?, but raises a descriptive error if possible-pagetree is invalid, and otherwise returns possible-pagetree itself.

Examples:

> (validate-pagetree '(root (mama.html son.html daughter.html) uncle.html))

'(root (mama.html son.html daughter.html) uncle.html)

> (validate-pagetree `(root (,+ son.html daughter.html) uncle.html))

#f

> (validate-pagetree '(root (mama.html son.html son.html) mama.html))

validate-pagetree: members-unique? failed because items

aren’t unique: (mama.html son.html)

procedure

(pagenode? possible-pagenode)  boolean?

  possible-pagenode : any/c
Test whether possible-pagenode is a valid pagenode. A pagenode can be any symbol? that is not whitespace/nbsp? Every leaf of a pagetree is a pagenode. In practice, your pagenodes will likely be names of output files.

Pagenodes are symbols (rather than strings) so that pagetrees will be valid tagged X-expressions, which is a more convenient format for validation & processing.

Examples:

; Three symbols, the third one annoying but valid
> (map pagenode? '(symbol index.html |   silly   |))

'(#t #t #t)

; A number, a string, a txexpr, and a whitespace symbol
> (map pagenode? '(9.999 "index.html" (p "Hello") |    |))

'(#f #f #f #f)

procedure

(pagenodeish? v)  boolean?

  v : any/c
Return #t if v can be converted with ->pagenode.

Example:

> (map pagenodeish? '(9.999 "index.html" |    |))

'(#t #t #f)

procedure

(->pagenode v)  pagenode?

  v : pagenodeish?
Convert v to a pagenode.

Examples:

> (map pagenodeish? '(symbol 9.999 "index.html" |  silly  |))

'(#t #t #t #t)

> (map ->pagenode '(symbol 9.999 "index.html" |  silly  |))

'(symbol |9.999| index.html |  silly  |)

11.4.6.2 Navigation

parameter

(current-pagetree)  pagetree?

(current-pagetree pagetree)  void?
  pagetree : pagetree?
A parameter that defines the default pagetree used by pagetree navigation functions (e.g., parent-pagenode, chidren, et al.) if another is not explicitly specified. Initialized to #f.

procedure

(parent p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the parent pagenode of p within pagetree. Return #f if there isn’t one.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (parent 'son.html)

'mama.html

> (parent "mama.html")

'root

> (parent (parent 'son.html))

'root

> (parent (parent (parent 'son.html)))

#f

procedure

(children p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the child pagenodes of p within pagetree. Return #f if there aren’t any.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (children 'mama.html)

'(son.html daughter.html)

> (children 'uncle.html)

#f

> (children 'root)

'(mama.html uncle.html)

> (map children (children 'root))

'((son.html daughter.html) #f)

procedure

(siblings p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the sibling pagenodes of p within pagetree. The list will include p itself. But the function will still return #f if pagetree is #f.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (siblings 'son.html)

'(son.html daughter.html)

> (siblings 'daughter.html)

'(son.html daughter.html)

> (siblings 'mama.html)

'(mama.html uncle.html)

procedure

(previous p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)

procedure

(previous* p [pagetree])  (or/c #f (listof pagenode?))

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Return the pagenode immediately before p. For previous*, return all the pagenodes before p, in sequence. In both cases, return #f if there aren’t any pagenodes. The root pagenode is ignored.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (previous 'daughter.html)

'son.html

> (previous 'son.html)

'mama.html

> (previous (previous 'daughter.html))

'mama.html

> (previous 'mama.html)

#f

> (previous* 'daughter.html)

'(mama.html son.html)

> (previous* 'uncle.html)

'(mama.html son.html daughter.html)

procedure

(next p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)

procedure

(next* p [pagetree])  (or/c #f (listof pagenode?))

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Return the pagenode immediately after p. For next*, return all the pagenodes after p, in sequence. In both cases, return #f if there aren’t any pagenodes. The root pagenode is ignored.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (next 'son.html)

'daughter.html

> (next 'daughter.html)

'uncle.html

> (next (next 'son.html))

'uncle.html

> (next 'uncle.html)

#f

> (next* 'mama.html)

'(son.html daughter.html uncle.html)

> (next* 'daughter.html)

'(uncle.html)

11.4.6.3 Utilities

procedure

(pagetree->list pagetree)  list?

  pagetree : pagetree?
Convert pagetree to a simple list. Equivalent to a pre-order depth-first traversal of pagetree.

procedure

(in-pagetree? pagenode [pagetree])  boolean?

  pagenode : pagenode?
  pagetree : pagetree? = (current-pagetree)
Report whether pagenode is in pagetree.

procedure

(path->pagenode p)  pagenode?

  p : pathish?
Convert path p to a pagenode — meaning, make it relative to current-project-root, run it through ->output-path, and convert it to a symbol. Does not tell you whether the resultant pagenode actually exists in the current pagetree (for that, use in-pagetree?).

 
\ No newline at end of file diff --git a/doc/doc-index.html b/doc/doc-index.html index 3da9259..0a52200 100644 --- a/doc/doc-index.html +++ b/doc/doc-index.html @@ -1,2 +1,2 @@ -Index

Index

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

 

“But I really need XML…”
“Now you have two problems”
#%top
->html
->markup-source-path
->null-source-path
->output-path
->pagenode
->preproc-source-path
->scribble-source-path
->template-source-path
A special data structure for HTML
Acknowledgments
Adding commands
Adding navigation links to the template with here
Any command is valid
Attaching behavior to tags
Attributes
Authoring mode
Backstory
Block
block-txexpr?
Cache
cache-ref
cached-require
children
Choosing custom tags
Command syntax using ◊
Creating a Pollen markup file
Creating a source file
Creating valid HTML output
current-cache
current-pagetree
Custom exports
Decode
decode
def/c
Defining variables with commands
detect-linebreaks
detect-paragraphs
Development environment
Enter Racket
File
File formats
First tutorial
First tutorial complete
Format independence
Functions
Further reading
get-template-for
Handling navigation boundaries with conditionals
has-markup-source?
has-null-source?
has-preproc-source?
has-scribble-source?
has-template-source?
has/is-markup-source?
has/is-null-source?
has/is-preproc-source?
has/is-scribble-source?
has/is-template-source?
in-pagetree?
index.ptree & the project server
Inserting a comment
Inserting specific source data into templates
Inserting the value of a variable
Inserting values from variables
Inserting variables within CSS
Installation
Intermission
Intermission
Intermission
Interpolating variables into strings
Invoking other functions
Invoking tag functions
License & source code
Linking to an external CSS file
make-cache
make-default-tag-function
Making a custom template
Making a pagetree file
Making pagetrees by hand
Making pagetrees with a source file
Making sure raco pollen works
Markdown (.pmd extension)
Markdown authoring mode
Markdown in Pollen: two options
Markdown mode
Markup (.pm extension)
Markup mode
markup-source?
Module reference
Multiple input values & rest arguments
Naming, saving, and rendering a source file
Navigation
next
next*
Notes for experienced programmers
Null (.p extension)
null-source?
One language, multiple dialects
Organizing functions
pagenode?
pagenodeish?
Pagetree
Pagetree (.ptree extension)
Pagetree navigation
pagetree->list
pagetree-source?
pagetree?
Pagetrees
Pagetrees
parent
Parsing attributes
path->pagenode
Point of no return
pollen
Pollen as a preprocessor
Pollen command syntax
Pollen markup vs. XML
pollen/cache
pollen/decode
pollen/file
pollen/markdown
pollen/markup
pollen/pagetree
pollen/pre
pollen/ptree
pollen/render
pollen/tag
pollen/template
pollen/top
pollen/world
Pollen: the book is a program
Predicates & validation
Prelude: my principled objection to Markdown
preproc-source?
Preprocessor (.pp extension)
Prerequisites
Prerequisites
Prerequisites
previous
previous*
project-block-tags
PS for Scribble users
Putting in the text of the poem
Putting it all together
Quick tour
Racket basics (if you’re not familiar)
raco pollen
raco pollen clone
raco pollen help
raco pollen render
raco pollen start
register-block-tag
Render
render
render-batch
render-pagetree
render-to-file
render-to-file-if-needed
reset-cache
Rethinking the solution for digital books
Returning an X-expression
Running a source file
Saving & naming your source file
Scribble (.scrbl extension)
scribble-source?
Second tutorial
Second tutorial complete
select
select*
select-from-doc
select-from-metas
Semantic markup
Setting the #lang line
Setting up a preprocessor source file
siblings
smart-dashes
smart-quotes
Source files in the dashboard
Source formats
split-attributes
Standard exports
Starting a new file in DrRacket
Starting the project server with raco pollen
Tag
Tags & tag functions
Tags are functions
Template
template-source?
Templated source files
Templates
Templates
The ->html function and the doc variable
The better idea: a programming model
The big picture
The book is a program
The command name
The directory-require.rkt file
The directory-require.rkt file
The end of the beginning
The golden rule
The lozenge glyph (◊)
The preprocessor
The project server
The Racket arguments
The relationship of Racket & Pollen
The text argument
The two command modes: text mode & Racket mode
The XML problem
Third tutorial
Top
Typography
Using custom tags
Using index.ptree in the dashboard
Using Markdown with the preprocessor
Using pagetrees for navigation
Using pagetrees with raco pollen render
Using Racket’s function libraries
Using raco pollen
Using the automatic pagetree
Using the dashboard
Using the project server
Utilities
Utility formats
validate-pagetree
Web development and its discontents
What are custom tags good for?
What is Pollen?
What Pollen markup does differently
when/block
whitespace/nbsp?
whitespace?
Working with the preprocessor
World
world:check-directory-requires-in-render?
world:command-marker
world:current-server-extras-path
world:current-server-port
world:dashboard-css
world:decodable-extensions
world:default-pagetree
world:default-port
world:default-template-prefix
world:directory-require
world:fallback-template-prefix
world:linebreak-separator
world:main-pollen-export
world:markdown-source-ext
world:markup-source-ext
world:meta-pollen-export
world:mode-auto
world:mode-markdown
world:mode-markup
world:mode-pagetree
world:mode-preproc
world:newline
world:null-source-ext
world:pagetree-root-node
world:pagetree-source-ext
world:paragraph-separator
world:paths-excluded-from-dashboard
world:preproc-source-ext
world:scribble-source-ext
world:server-extras-dir
world:template-meta-key
world:template-source-ext
wrap-hanging-quotes
Writing with Pollen markup
X-expressions
◊ command overview

 
\ No newline at end of file +Index

Index

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

 

“But I really need XML…”
“Now you have two problems”
#%top
->html
->markup-source-path
->null-source-path
->output-path
->pagenode
->preproc-source-path
->scribble-source-path
->template-source-path
A special data structure for HTML
Acknowledgments
Adding commands
Adding navigation links to the template with here
Any command is valid
Attaching behavior to tags
Attributes
Authoring mode
Backstory
Block
block-txexpr?
Cache
cache-ref
cached-require
children
Choosing custom tags
Command syntax using ◊
Creating a Pollen markup file
Creating a source file
Creating valid HTML output
current-cache
current-pagetree
Custom exports
Decode
decode
def/c
Defining variables with commands
detect-linebreaks
detect-paragraphs
Development environment
Enter Racket
File
File formats
First tutorial
First tutorial complete
Format independence
Functions
Further reading
get-template-for
Handling navigation boundaries with conditionals
has-markup-source?
has-null-source?
has-preproc-source?
has-scribble-source?
has-template-source?
has/is-markup-source?
has/is-null-source?
has/is-preproc-source?
has/is-scribble-source?
has/is-template-source?
in-pagetree?
index.ptree & the project server
Inserting a comment
Inserting metas
Inserting specific source data into templates
Inserting the value of a variable
Inserting values from variables
Inserting variables within CSS
Installation
Intermission
Intermission
Intermission
Interpolating variables into strings
Invoking other functions
Invoking tag functions
License & source code
Linking to an external CSS file
make-cache
make-default-tag-function
Making a custom template
Making a pagetree file
Making pagetrees by hand
Making pagetrees with a source file
Making sure raco pollen works
Markdown (.pmd extension)
Markdown authoring mode
Markdown in Pollen: two options
Markdown mode
Markup (.pm extension)
Markup mode
markup-source?
Module reference
Multiple input values & rest arguments
Naming, saving, and rendering a source file
Navigation
next
next*
Notes for experienced programmers
Null (.p extension)
null-source?
One language, multiple dialects
Organizing functions
pagenode?
pagenodeish?
Pagetree
Pagetree (.ptree extension)
Pagetree navigation
pagetree->list
pagetree-source?
pagetree?
Pagetrees
Pagetrees
parent
Parsing attributes
path->pagenode
Point of no return
pollen
Pollen as a preprocessor
Pollen command syntax
Pollen markup vs. XML
pollen/cache
pollen/decode
pollen/file
pollen/markdown
pollen/markup
pollen/pagetree
pollen/pre
pollen/ptree
pollen/render
pollen/tag
pollen/template
pollen/top
pollen/world
Pollen: the book is a program
Predicates & validation
Prelude: my principled objection to Markdown
preproc-source?
Preprocessor (.pp extension)
Prerequisites
Prerequisites
Prerequisites
previous
previous*
project-block-tags
PS for Scribble users
Putting in the text of the poem
Putting it all together
Quick tour
Racket basics (if you’re not familiar)
raco pollen
raco pollen clone
raco pollen help
raco pollen render
raco pollen start
register-block-tag
Render
render
render-batch
render-pagetree
render-to-file
render-to-file-if-needed
reset-cache
Rethinking the solution for digital books
Returning an X-expression
Running a source file
Saving & naming your source file
Scribble (.scrbl extension)
scribble-source?
Second tutorial
Second tutorial complete
select
select*
select-from-doc
select-from-metas
Semantic markup
Setting the #lang line
Setting up a preprocessor source file
siblings
smart-dashes
smart-quotes
Source files in the dashboard
Source formats
split-attributes
Standard exports
Starting a new file in DrRacket
Starting the project server with raco pollen
Tag
Tags & tag functions
Tags are functions
Template
template-source?
Templated source files
Templates
Templates
The ->html function and the doc variable
The better idea: a programming model
The big picture
The book is a program
The command name
The directory-require.rkt file
The directory-require.rkt file
The end of the beginning
The golden rule
The lozenge glyph (◊)
The preprocessor
The project server
The Racket arguments
The relationship of Racket & Pollen
The text argument
The two command modes: text mode & Racket mode
The XML problem
Third tutorial
Top
Typography
Using custom tags
Using index.ptree in the dashboard
Using Markdown with the preprocessor
Using pagetrees for navigation
Using pagetrees with raco pollen render
Using Racket’s function libraries
Using raco pollen
Using the automatic pagetree
Using the dashboard
Using the project server
Utilities
Utility formats
validate-pagetree
Web development and its discontents
What are custom tags good for?
What is Pollen?
What Pollen markup does differently
when/block
whitespace/nbsp?
whitespace?
Working with the preprocessor
World
world:check-directory-requires-in-render?
world:command-marker
world:current-server-extras-path
world:current-server-port
world:dashboard-css
world:decodable-extensions
world:default-pagetree
world:default-port
world:default-template-prefix
world:directory-require
world:fallback-template-prefix
world:linebreak-separator
world:main-pollen-export
world:markdown-source-ext
world:markup-source-ext
world:meta-pollen-export
world:mode-auto
world:mode-markdown
world:mode-markup
world:mode-pagetree
world:mode-preproc
world:newline
world:null-source-ext
world:pagetree-root-node
world:pagetree-source-ext
world:paragraph-separator
world:paths-excluded-from-dashboard
world:preproc-source-ext
world:scribble-source-ext
world:server-extras-dir
world:template-meta-key
world:template-source-ext
wrap-hanging-quotes
Writing with Pollen markup
X-expressions
◊ command overview

 
\ No newline at end of file diff --git a/doc/index.html b/doc/index.html index edfafe1..32f79a5 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,3 +1,3 @@ Pollen: the book is a program
On this page:
Pollen:   the book is a program
6.1.0.5

Pollen: the book is a program

Matthew Butterick <mb@mbtype.com>

 #lang pollen package: pollen

Pollen is a publishing system that helps authors create beautiful and functional web-based books. Pollen includes tools for writing, designing, programming, testing, and publishing.

I used Pollen to create my book Butterick’s Practical Typography. Sure, go take a look. Is it better than the last digital book you encountered? Yes it is. Would you like your book to look like that? If so, keep reading.

At the core of Pollen is an argument: -
  • First, that digital books should be the best books we’ve ever had. So far, they’re not even close.

  • Second, that because digital books are software, an author shouldn’t think of a book as merely data. The book is a program.

  • Third, that the way we make digital books better than their predecessors is by exploiting this programmability.

That’s what Pollen is for.

Not that you need to be a programmer to use Pollen. On the contrary, the Pollen language is markup-based, so you can write & edit text naturally. But when you want to automate repetitive tasks, add cross-references, or pull in data from other sources, you can access a full programming language from within the text.

That language is Racket. I chose Racket because while the idea for Pollen had been with me for several years, it simply wasn’t possible to build it with other languages. So if it’s unfamiliar to you, don’t panic. It was unfamiliar to me. Once you see what you can do with Pollen & Racket, you may be persuaded. I was.

Or, if you can find a better digital-publishing tool, use that. But I’m never going back to the way I used to work.

    1 Installation

    2 Quick tour

      2.1 Creating a source file

      2.2 Running a source file

      2.3 Naming, saving, and rendering a source file

      2.4 The project server

      2.5 Intermission

      2.6 Pollen as a preprocessor

      2.7 Markdown mode

      2.8 Markup mode

      2.9 Templates

      2.10 PS for Scribble users

      2.11 The end of the beginning

    3 Backstory

      3.1 Web development and its discontents

      3.2 The better idea: a programming model

      3.3 “Now you have two problems”

      3.4 Rethinking the solution for digital books

      3.5 Enter Racket

      3.6 What is Pollen?

    4 The big picture

      4.1 The book is a program

      4.2 One language, multiple dialects

      4.3 Development environment

      4.4 A special data structure for HTML

      4.5 Pollen command syntax

      4.6 The preprocessor

      4.7 Templated source files

      4.8 Pagetrees

    5 First tutorial

      5.1 Prerequisites

      5.2 The relationship of Racket & Pollen

      5.3 Starting a new file in DrRacket

        5.3.1 Setting the #lang line

        5.3.2 Putting in the text of the poem

        5.3.3 Saving & naming your source file

      5.4 Using the project server

        5.4.1 Starting the project server with raco pollen

        5.4.2 Using the dashboard

        5.4.3 Source files in the dashboard

      5.5 Working with the preprocessor

        5.5.1 Setting up a preprocessor source file

        5.5.2 Creating valid HTML output

        5.5.3 Adding commands

        5.5.4 Racket basics (if you’re not familiar)

        5.5.5 Defining variables with commands

        5.5.6 Inserting values from variables

        5.5.7 Inserting variables within CSS

      5.6 First tutorial complete

    6 Second tutorial

      6.1 Prerequisites

      6.2 Prelude: my principled objection to Markdown

      6.3 Markdown in Pollen: two options

        6.3.1 Using Markdown with the preprocessor

        6.3.2 Authoring mode

        6.3.3 X-expressions

        6.3.4 Markdown authoring mode

      6.4 Templates

        6.4.1 The ->html function and the doc variable

        6.4.2 Making a custom template

        6.4.3 Inserting specific source data into templates

        6.4.4 Linking to an external CSS file

      6.5 Intermission

      6.6 Pagetrees

        6.6.1 Pagetree navigation

        6.6.2 Using the automatic pagetree

        6.6.3 Adding navigation links to the template with here

        6.6.4 Handling navigation boundaries with conditionals

        6.6.5 Making a pagetree file

        6.6.6 index.ptree & the project server

      6.7 Second tutorial complete

    7 Third tutorial

      7.1 Prerequisites

      7.2 Pollen markup vs. XML

        7.2.1 The XML problem

        7.2.2 What Pollen markup does differently

        7.2.3 “But I really need XML…”

      7.3 Writing with Pollen markup

        7.3.1 Creating a Pollen markup file

        7.3.2 Tags & tag functions

        7.3.3 Attributes

        7.3.4 What are custom tags good for?

        7.3.5 Semantic markup

        7.3.6 Format independence

        7.3.7 Using custom tags

        7.3.8 Choosing custom tags

      7.4 Tags are functions

        7.4.1 Attaching behavior to tags

        7.4.2 Notes for experienced programmers

          7.4.2.1 Point of no return

          7.4.2.2 Multiple input values & rest arguments

          7.4.2.3 Returning an X-expression

          7.4.2.4 Interpolating variables into strings

          7.4.2.5 Parsing attributes

      7.5 Intermission

      7.6 Organizing functions

        7.6.1 Using Racket’s function libraries

        7.6.2 The directory-require.rkt file

      7.7 Putting it all together

    8 Using raco pollen

      8.1 Making sure raco pollen works

      8.2 raco pollen

      8.3 raco pollen help

      8.4 raco pollen start

      8.5 raco pollen render

      8.6 raco pollen clone

    9 File formats

      9.1 Source formats

        9.1.1 Command syntax using ◊

        9.1.2 Any command is valid

        9.1.3 Standard exports

        9.1.4 Custom exports

        9.1.5 The directory-require.rkt file

        9.1.6 Preprocessor (.pp extension)

        9.1.7 Markdown (.pmd extension)

        9.1.8 Markup (.pm extension)

        9.1.9 Pagetree (.ptree extension)

      9.2 Utility formats

        9.2.1 Scribble (.scrbl extension)

        9.2.2 Null (.p extension)

    10 ◊ command overview

      10.1 The golden rule

      10.2 The lozenge glyph (◊)

      10.3 The two command modes: text mode & Racket mode

        10.3.1 The command name

          10.3.1.1 Invoking tag functions

          10.3.1.2 Invoking other functions

          10.3.1.3 Inserting the value of a variable

          10.3.1.4 Inserting a comment

        10.3.2 The Racket arguments

        10.3.3 The text argument

      10.4 Further reading

    11 Module reference

      11.1 Cache

      11.2 Decode

        11.2.1 Block

        11.2.2 Typography

      11.3 File

      11.4 Pagetree

        11.4.1 Making pagetrees with a source file

        11.4.2 Making pagetrees by hand

        11.4.3 Using pagetrees for navigation

        11.4.4 Using index.ptree in the dashboard

        11.4.5 Using pagetrees with raco pollen render

        11.4.6 Functions

          11.4.6.1 Predicates & validation

          11.4.6.2 Navigation

          11.4.6.3 Utilities

      11.5 Render

      11.6 Template

      11.7 Tag

      11.8 Top

      11.9 World

    12 Acknowledgments

    13 License & source code

    Index

 
\ No newline at end of file +

That’s what Pollen is for.

Not that you need to be a programmer to use Pollen. On the contrary, the Pollen language is markup-based, so you can write & edit text naturally. But when you want to automate repetitive tasks, add cross-references, or pull in data from other sources, you can access a full programming language from within the text.

That language is Racket. I chose Racket because while the idea for Pollen had been with me for several years, it simply wasn’t possible to build it with other languages. So if it’s unfamiliar to you, don’t panic. It was unfamiliar to me. Once you see what you can do with Pollen & Racket, you may be persuaded. I was.

Or, if you can find a better digital-publishing tool, use that. But I’m never going back to the way I used to work.

    1 Installation

    2 Quick tour

      2.1 Creating a source file

      2.2 Running a source file

      2.3 Naming, saving, and rendering a source file

      2.4 The project server

      2.5 Intermission

      2.6 Pollen as a preprocessor

      2.7 Markdown mode

      2.8 Markup mode

      2.9 Templates

      2.10 PS for Scribble users

      2.11 The end of the beginning

    3 Backstory

      3.1 Web development and its discontents

      3.2 The better idea: a programming model

      3.3 “Now you have two problems”

      3.4 Rethinking the solution for digital books

      3.5 Enter Racket

      3.6 What is Pollen?

    4 The big picture

      4.1 The book is a program

      4.2 One language, multiple dialects

      4.3 Development environment

      4.4 A special data structure for HTML

      4.5 Pollen command syntax

      4.6 The preprocessor

      4.7 Templated source files

      4.8 Pagetrees

    5 First tutorial

      5.1 Prerequisites

      5.2 The relationship of Racket & Pollen

      5.3 Starting a new file in DrRacket

        5.3.1 Setting the #lang line

        5.3.2 Putting in the text of the poem

        5.3.3 Saving & naming your source file

      5.4 Using the project server

        5.4.1 Starting the project server with raco pollen

        5.4.2 Using the dashboard

        5.4.3 Source files in the dashboard

      5.5 Working with the preprocessor

        5.5.1 Setting up a preprocessor source file

        5.5.2 Creating valid HTML output

        5.5.3 Adding commands

        5.5.4 Racket basics (if you’re not familiar)

        5.5.5 Defining variables with commands

        5.5.6 Inserting values from variables

        5.5.7 Inserting variables within CSS

      5.6 First tutorial complete

    6 Second tutorial

      6.1 Prerequisites

      6.2 Prelude: my principled objection to Markdown

      6.3 Markdown in Pollen: two options

        6.3.1 Using Markdown with the preprocessor

        6.3.2 Authoring mode

        6.3.3 X-expressions

        6.3.4 Markdown authoring mode

      6.4 Templates

        6.4.1 The ->html function and the doc variable

        6.4.2 Making a custom template

        6.4.3 Inserting specific source data into templates

        6.4.4 Linking to an external CSS file

      6.5 Intermission

      6.6 Pagetrees

        6.6.1 Pagetree navigation

        6.6.2 Using the automatic pagetree

        6.6.3 Adding navigation links to the template with here

        6.6.4 Handling navigation boundaries with conditionals

        6.6.5 Making a pagetree file

        6.6.6 index.ptree & the project server

      6.7 Second tutorial complete

    7 Third tutorial

      7.1 Prerequisites

      7.2 Pollen markup vs. XML

        7.2.1 The XML problem

        7.2.2 What Pollen markup does differently

        7.2.3 “But I really need XML…”

      7.3 Writing with Pollen markup

        7.3.1 Creating a Pollen markup file

        7.3.2 Tags & tag functions

        7.3.3 Attributes

        7.3.4 What are custom tags good for?

        7.3.5 Semantic markup

        7.3.6 Format independence

        7.3.7 Using custom tags

        7.3.8 Choosing custom tags

      7.4 Tags are functions

        7.4.1 Attaching behavior to tags

        7.4.2 Notes for experienced programmers

          7.4.2.1 Point of no return

          7.4.2.2 Multiple input values & rest arguments

          7.4.2.3 Returning an X-expression

          7.4.2.4 Interpolating variables into strings

          7.4.2.5 Parsing attributes

      7.5 Intermission

      7.6 Organizing functions

        7.6.1 Using Racket’s function libraries

        7.6.2 The directory-require.rkt file

      7.7 Putting it all together

    8 Using raco pollen

      8.1 Making sure raco pollen works

      8.2 raco pollen

      8.3 raco pollen help

      8.4 raco pollen start

      8.5 raco pollen render

      8.6 raco pollen clone

    9 File formats

      9.1 Source formats

        9.1.1 Command syntax using ◊

        9.1.2 Any command is valid

        9.1.3 Standard exports

        9.1.4 Custom exports

        9.1.5 The directory-require.rkt file

        9.1.6 Preprocessor (.pp extension)

        9.1.7 Markdown (.pmd extension)

        9.1.8 Markup (.pm extension)

        9.1.9 Pagetree (.ptree extension)

      9.2 Utility formats

        9.2.1 Scribble (.scrbl extension)

        9.2.2 Null (.p extension)

    10 ◊ command overview

      10.1 The golden rule

      10.2 The lozenge glyph (◊)

      10.3 The two command modes: text mode & Racket mode

        10.3.1 The command name

          10.3.1.1 Invoking tag functions

          10.3.1.2 Invoking other functions

          10.3.1.3 Inserting the value of a variable

          10.3.1.4 Inserting metas

          10.3.1.5 Inserting a comment

        10.3.2 The Racket arguments

        10.3.3 The text argument

      10.4 Further reading

    11 Module reference

      11.1 Cache

      11.2 Decode

        11.2.1 Block

        11.2.2 Typography

      11.3 File

      11.4 Pagetree

        11.4.1 Making pagetrees with a source file

        11.4.2 Making pagetrees by hand

        11.4.3 Using pagetrees for navigation

        11.4.4 Using index.ptree in the dashboard

        11.4.5 Using pagetrees with raco pollen render

        11.4.6 Functions

          11.4.6.1 Predicates & validation

          11.4.6.2 Navigation

          11.4.6.3 Utilities

      11.5 Render

      11.6 Template

      11.7 Tag

      11.8 Top

      11.9 World

    12 Acknowledgments

    13 License & source code

    Index

 
\ No newline at end of file diff --git a/doc/reader.html b/doc/reader.html index 3b88c5b..d61a675 100644 --- a/doc/reader.html +++ b/doc/reader.html @@ -1,28 +1,2 @@ -10 ◊ command overview
On this page:
10.1 The golden rule
10.2 The lozenge glyph (◊)
10.3 The two command modes:   text mode & Racket mode
10.3.1 The command name
10.3.1.1 Invoking tag functions
10.3.1.2 Invoking other functions
10.3.1.3 Inserting the value of a variable
10.3.1.4 Inserting a comment
10.3.2 The Racket arguments
10.3.3 The text argument
10.4 Further reading
6.1.0.5

10 ◊ command overview

10.1 The golden rule

Pollen uses a special character — the 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.

10.2 The lozenge glyph (◊)

I chose the lozenge as the command marker 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:

Mac: option + shift + V -
-Windows: holding down alt, type 9674 on the num pad -
-Ubuntu: ctrl + shift + U, then 25CA

Still, if you don’t want to use the lozenge as your command marker, you can use something else. Set Pollen’s world:command-marker value to whatever character you want.

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.

But don’t knock the lozenge till you try it.

10.3 The two command modes: text mode & Racket mode

Pollen commands can be entered in one of two modes: text mode or Racket mode. Both modes start with a lozenge ():

 command name [ Racket arguments ... ] { text argument }
 ( Racket expression )

Text-mode commands

A text-mode command has the three possible parts after the :

Each of the three parts is optional. You can also nest commands within each other. However:

Here are a few examples of correct text-mode commands:

#lang pollen
variable-name
tag{Text inside the tag.}
tag['attr: "value"]{Text inside the tag}
get-customer-id["Brennan Huff"]
tag{His ID is get-customer-id["Brennan Huff"].}

And some incorrect examples:

#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}['attr: "value"] ; wrong order

The next section describes each of these parts in detail.

Racket-mode 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 to the front. So in Racket, this code:

#lang racket
(define song "Revolution")
(format "~a #~a" song (* 3 3))

Can be converted to Pollen like so:

#lang pollen
(define song "Revolution")
(format "~a #~a" song (* 3 3))

And in DrRacket, they produce the same output:

Revolution #9

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.

The relationship of text mode and Racket mode

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 text mode to Racket mode. So a text-mode command that looks like this:

◊headline[#:size 'enormous]{Man Bites Dog!}

Is actually being turned into a Racket-mode command like this:

(headline #:size 'enormous "Man Bites Dog!")

Thus a text-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.)

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:

#lang pollen
(define song "Revolution")
(format "~a #~a" song (* 3 3))

Can be rewritten using text mode:

#lang pollen
define[song]{Revolution}
format["~a #~a" song (* 3 3)]

And it will work the same way.

10.3.1 The command name

In Pollen, you’ll typically use the command name for one of four purposes:

10.3.1.1 Invoking tag functions

By default, Pollen treats every command name as a tag function. As the name implies, a tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content.

#lang pollen
strong{Fancy Sauce, $1}

'(strong "Fancy Sauce, $1")

To streamline markup, Pollen doesn’t restrict you to a certain set of tags, nor does it make you define your tag functions ahead of time. Just type a tag, and you can start using it.

#lang pollen
utterlyridiculoustagname{Oh really?}
'(utterlyridiculoustagname "Oh really?")

The one restriction is that you can’t invent names for tag functions that are already being used for other commands. For instance, map is a name permanently reserved by the Racket function map. It’s also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other map:

#lang pollen
map{Fancy Sauce, $1}

map: arity mismatch;
-the expected number of arguments does not match the given number
-  given: 1
-  arguments...:
-    "Fancy Sauce, $1"

What to do? Read on.

10.3.1.2 Invoking other functions

Though every command name starts out as a tag function, it doesn’t necessarily end there. You have two options for invoking other functions: defining your own , or invoking others from Racket.

Defining your own functions

Use the define command to create your own function for a command name. After that, when you use the command name, you’ll get the new behavior. For instance, recall this example showing the default tag-function behavior:

#lang pollen
strong{Fancy Sauce, $1}

'(strong "Fancy Sauce, $1")

We can define strong to do something else, like add to the text:

#lang pollen
(define (strong text) `(strong ,(format "Hey! Listen up! ~a" text)))
strong{Fancy Sauce, $1}

'(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 strong is going to get a text argument that it’s not defined to handle:

#lang pollen
(define (strong) '(fib "1 1 2 3 5 8 13 ..."))
strong{Fancy Sauce, $1}

strong: arity mismatch;
-the expected number of arguments does not match the given number
-  expected: 0
-  given: 1
-  arguments...:
-    "Fancy Sauce, $1"

Whereas in this version, strong accepts an argument called text, but then ignores it:

#lang pollen
(define (strong text) '(fib "1 1 2 3 5 8 13 ..."))
strong{Fancy Sauce, $1}

'(fib "1 1 2 3 5 8 13 ...")

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.

Using Racket functions

You aren’t limited to functions you define. Any function from Racket, or any Racket library, can be invoked directly by using it as a command name. Here’s the function range, which creates a list of numbers:

#lang pollen
range[1 20]

'(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 racket/list library, which contains the definition for range. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without racket/list, Pollen just thinks we’re trying to use range as a tag function (and if we had been, then '(range 1 20) would’ve been the right result).

We fix this by using the require command to bring in the racket/list library, which contains the range we want:

#lang pollen
(require racket/list)
range[1 20]

'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names:

#lang pollen
(require racket/list)
(define (rick start finish) (range start finish))
rick[1 20]

'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

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 map as a tag even though Racket is using it for its own function called map. First, we invent a command name that doesn’t conflict. Let’s call it my-map. As you learned above, Pollen will treat a new command name as a tag function by default:

#lang pollen
my-map{How I would love this to be a map.}

'(my-map "How I would love this to be a map.")

But my-map is not the tag we want. We need to define my-map to be a tag function for map. We can do this with the Pollen helper make-default-tag-function. That function lives in pollen/tag, so we require that too:

#lang pollen
(require pollen/tag)
(define my-map (make-default-tag-function 'map))
my-map{How I would love this to be a map.}

'(map "How I would love this to be a map.")

Problem solved.

10.3.1.3 Inserting the value of a variable

A Pollen command name usually refers to a function, but it can also refer to a 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:

#lang pollen
(define foo "bar")
The value of foo is foo

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:

To understand what happens here, recall the relationship between Pollen’s command modes. The text-mode command ◊foo[] becomes the Racket-mode command (foo), which after variable substitution becomes ("bar"). If you try to evaluate ("bar") — e.g., in DrRacket — you’ll get the same error.

#lang pollen
(define foo "bar")
The value of foo is foo[]

application: not a procedure;
-expected a procedure that can be applied to arguments
-  given: "bar"
-  arguments...: [none]

The reason we can simply drop ◊foo into the text argument of another Pollen command is that the variable foo holds a string (i.e., a text value). When appropriate, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted:

#lang pollen
(define zam 42)
The value of zam is zam

The value of zam is 42

If the variable holds a container datatype (like a list, hash, or vector), Pollen will produce the Racket text representation of the item. Here, zam is a list of integers:

#lang pollen
(define zam (list 1 2 3))
The value of zam is zam

The value of zam is '(1 2 3)

This feature is included for your convenience as an author. But in general, your readers won’t want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using string-join from the racket/string library:

#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 1 and 2 and 3

Pollen will still produce an error if you try to convert an esoteric value to a string. Here, zam is the addition function (+):

#lang pollen
(define zam +)
The value of zam is zam

Pollen decoder: can’t convert #<procedure:+> to string

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 edge next to the string px:

#lang pollen
(define edge 100)
p { margin-left: edgepx; }

Pollen decoder: can’t convert #<procedure:...t/pollen/tag.rkt:6:2> to string

The example fails because Pollen reads the whole string after the as the single variable name edgepx. Since 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.

In these situations, surround the variable name with vertical bars ◊|like so| to explicitly indicate where the variable name ends. The bars are not treated as part of the name, nor are they included in the result. Once we do that, we get what we intended:

#lang pollen
(define edge 100)
p { margin-left: ◊|edge|px; }

p { margin-left: 100px; }

If you use this notation when you don’t need to, nothing bad will happen. The vertical bars are always ignored.

#lang pollen
(define edge 100)
The value of edge is ◊|edge| pixels}

The value of edge is 100 pixels

10.3.1.4 Inserting a comment

Two options.

To comment out the rest of a single line, use a lozenge followed by a semicolon ◊;.

#lang pollen
span{This is not a comment}
span{Nor is this} ;span{But this is}

'(span "This is not a comment")
-'(span "Nor is this")

To comment out a multiline block, use the lozenge–semicolon signal ◊; with curly braces, ◊;{like so}.

#lang pollen
;{
◊span{This is not a comment}
◊span{Nor is this} ;span{But this is}
}
Actually, it's all a comment now

Actually, it's all a comment now

10.3.2 The Racket arguments

The middle part of a text-mode Pollen command contains the Racket arguments [between square brackets.] 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:

#lang pollen
title{The Beginning of the End}

'(title "The Beginning of the End")

But what if you wanted to add attributes to this tag, so that it comes out like this?

'(title ((class "red")(id "first")) "The Beginning of the End")

You can do it with Racket arguments.

Here’s the hard way. You can type out your list of attributes in Racket format and drop them into the brackets as a single argument:

#lang pollen
title['((class "red")(id "first"))]{The Beginning of the End}

'(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 symbol / string pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark ' and end with a colon :. So taken together, they look like this:

#lang pollen
title['class: "red" 'id: "first"]{The Beginning of the End}

'(title ((class "red") (id "first")) "The Beginning of the End")

Racket arguments can be any valid Racket expressions. For instance, this will also work:

#lang pollen
title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End}

'(title ((class "42") (id "first")) "The Beginning of the End")

Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we’ll define a variable called name and use it in the Racket arguments of title:

#lang pollen
(define name "Brennan")
title['class: "red" 'id: name]{The Beginning of the End}

'(title ((class "read") (id "Brennan")) "The Beginning of the End")

You can also use this area for keyword arguments. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the h1 ... h6 tags, you want to consolidate them into one command called heading and select the level separately. You can do this with a keyword, in this case #:level, which is passed as a Racket argument:

#lang pollen
(define (heading #:level which text)
   `(,(string->symbol (format "h~a" which)) ,text))
 
heading[#:level 1]{Major league}
heading[#:level 2]{Minor league}
heading[#:level 6]{Trivial league}

'(h1 "Major league")
-'(h2 "Minor league")
-'(h6 "Trivial league")

10.3.3 The text argument

The third part of a text-mode Pollen command is the text argument. The text argument {appears between curly braces}. 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.

#lang pollen
div{Do it again. div{And again. div{And yet again.}}}

'(div "Do it again. " (div "And again. " (div "And yet again.")))

Three small details to know about the text argument.

First, the only character that needs special handling in a text argument is the lozenge . 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 ◊"◊".

#lang pollen
definition{This is the lozenge: "◊"}

'(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:

#lang pollen
div{
Roomy!
 
I agree.
}

'(div "Roomy!" "\n" "\n" "I agree.")

Yields the same result as this one:

#lang pollen
div{Roomy!
 
I agree.}

'(div "Roomy!" "\n" "\n" "I agree.")

For the long version, please see [future link: Spaces, Newlines, and Indentation].

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:

'(div "Roomy!" "\n" "\n" "I agree.")

Instead of this:

'(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 jejune function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:

#lang pollen
(define (jejune text)
   `(jejune ,text))
jejune{Irrational confidence}

'(jejune "Irrational confidence")

But watch what happens with a multiline text argument:

#lang pollen
(define (jejune text)
   `(jejune ,text))
jejune{Deeply
        chastened}

jejune: arity mismatch;
-the expected number of arguments does not match the given number
-  expected: 1
-  given: 3
-  arguments...:
-   "Deeply"
-   "\n"
-   "chastened"

The answer is to use a rest argument in the function, which takes the “rest” of the arguments — however many there may be — and combines them into a single list. If we rewrite jejune with a rest argument, we can fix the problem:

#lang pollen
(define (jejune . texts)
   `(jejune ,@texts))
jejune{Deeply
        chastened}

'(jejune "Deeply" "\n" "chastened")

10.4 Further reading

The Pollen language is a variant of Racket’s own text-processing language, called Scribble. So many things that are true about Scribble are also true about Pollen. For the sake of clarity & brevity, I’ve omitted them from this summary. But if you want the full story, see @ Syntax in the Scribble documentation.

 
\ No newline at end of file +10 ◊ command overview
On this page:
10.1 The golden rule
10.2 The lozenge glyph (◊)
10.3 The two command modes:   text mode & Racket mode
10.3.1 The command name
10.3.1.1 Invoking tag functions
10.3.1.2 Invoking other functions
10.3.1.3 Inserting the value of a variable
10.3.1.4 Inserting metas
10.3.1.5 Inserting a comment
10.3.2 The Racket arguments
10.3.3 The text argument
10.4 Further reading
6.1.0.5

10 ◊ command overview

10.1 The golden rule

Pollen uses a special character — the 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.

10.2 The lozenge glyph (◊)

I chose the lozenge as the command marker 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:

Mac: option + shift + V

Windows: holding down alt, type 9674 on the num pad

Ubuntu: ctrl + shift + U, then 25CA

Still, if you don’t want to use the lozenge as your command marker, you can use something else. Set Pollen’s world:command-marker value to whatever character you want.

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.

But don’t knock the lozenge till you try it.

10.3 The two command modes: text mode & Racket mode

Pollen commands can be entered in one of two modes: text mode or Racket mode. Both modes start with a lozenge ():

 command name [ Racket arguments ... ] { text argument }
 ( Racket expression )

Text-mode commands

A text-mode command has the three possible parts after the :

Each of the three parts is optional. You can also nest commands within each other. However:

Here are a few examples of correct text-mode commands:

#lang pollen
variable-name
tag{Text inside the tag.}
tag['attr: "value"]{Text inside the tag}
get-customer-id["Brennan Huff"]
tag{His ID is get-customer-id["Brennan Huff"].}

And some incorrect examples:

#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}['attr: "value"] ; wrong order

The next section describes each of these parts in detail.

Racket-mode 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 to the front. So in Racket, this code:

#lang racket
(define song "Revolution")
(format "~a #~a" song (* 3 3))

Can be converted to Pollen like so:

#lang pollen
(define song "Revolution")
(format "~a #~a" song (* 3 3))

And in DrRacket, they produce the same output:

Revolution #9

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.

The relationship of text mode and Racket mode

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 text mode to Racket mode. So a text-mode command that looks like this:

headline[#:size 'enormous]{Man Bites Dog!}

Is actually being turned into a Racket-mode command like this:

(headline #:size 'enormous "Man Bites Dog!")

Thus a text-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.)

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:

#lang pollen
(define song "Revolution")
(format "~a #~a" song (* 3 3))

Can be rewritten using text mode:

#lang pollen
define[song]{Revolution}
format["~a #~a" song (* 3 3)]

And it will work the same way.

10.3.1 The command name

In Pollen, you’ll typically use the command name for one of four purposes:

10.3.1.1 Invoking tag functions

By default, Pollen treats every command name as a 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.

#lang pollen
strong{Fancy Sauce, $1}

'(strong "Fancy Sauce, $1")

To streamline markup, Pollen doesn’t restrict you to a certain set of tags, nor does it make you define your tags ahead of time. Just type a tag, and you can start using it.

#lang pollen
utterlyridiculoustagname{Oh really?}

'(utterlyridiculoustagname "Oh really?")

The one restriction is that you can’t invent names for tags that are already being used for other commands. For instance, map is a name permanently reserved by the Racket function map. It’s also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other map:

#lang pollen
map{Fancy Sauce, $1}

map: arity mismatch;
the expected number of arguments does not match the given number
  given: 1
  arguments...:
    "Fancy Sauce, $1"

What to do? Read on.

10.3.1.2 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.

Defining your own functions

Use the define command to create your own function for a command name. After that, when you use the command name, you’ll get the new behavior. For instance, recall this example showing the default tag-function behavior:

#lang pollen
strong{Fancy Sauce, $1}

'(strong "Fancy Sauce, $1")

We can define strong to do something else, like add to the text:

#lang pollen
(define (strong text) `(strong ,(format "Hey! Listen up! ~a" text)))
strong{Fancy Sauce, $1}

'(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 strong is going to get a text argument that it’s not defined to handle:

#lang pollen
(define (strong) '(fib "1 1 2 3 5 8 13 ..."))
strong{Fancy Sauce, $1}

strong: arity mismatch;
the expected number of arguments does not match the given number
  expected: 0
  given: 1
  arguments...:
    "Fancy Sauce, $1"

Whereas in this version, strong accepts an argument called text, but then ignores it:

#lang pollen
(define (strong text) '(fib "1 1 2 3 5 8 13 ..."))
strong{Fancy Sauce, $1}

'(fib "1 1 2 3 5 8 13 ...")

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.

Using Racket functions

You aren’t limited to functions you define. Any function from Racket, or any Racket library, can be invoked directly by using it as a command name. Here’s the function range, which creates a list of numbers:

#lang pollen
range[1 20]

'(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 racket/list library, which contains the definition for range. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without racket/list, Pollen just thinks we’re trying to use range as a tag function (and if we had been, then

'(range 1 20)

would’ve been the right result).

We fix this by using the require command to bring in the racket/list library, which contains the range we want:

#lang pollen
(require racket/list)
range[1 20]

'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names:

#lang pollen
(require racket/list)
(define (rick start finish) (range start finish))
rick[1 20]

'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

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 map as a tag even though Racket is using it for its own function called map. First, we invent a command name that doesn’t conflict. Let’s call it my-map. As you learned above, Pollen will treat a new command name as a tag function by default:

#lang pollen
my-map{How I would love this to be a map.}

'(my-map "How I would love this to be a map.")

But my-map is not the tag we want. We need to define my-map to be a tag function for map. We can do this with the Pollen helper make-default-tag-function. That function lives in pollen/tag, so we require that too:

#lang pollen
(require pollen/tag)
(define my-map (make-default-tag-function 'map))
my-map{How I would love this to be a map.}

'(map "How I would love this to be a map.")

Problem solved.

10.3.1.3 Inserting the value of a variable

A Pollen command name usually refers to a function, but it can also refer to a 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:

#lang pollen
(define foo "bar")
The value of foo is foo

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:

To understand what happens here, recall the relationship between Pollen’s command modes. The text-mode command ◊foo[] becomes the Racket-mode command (foo), which after variable substitution becomes ("bar"). If you try to evaluate ("bar") — e.g., in DrRacket — you’ll get the same error.

#lang pollen
(define foo "bar")
The value of foo is foo[]

application: not a procedure;
expected a procedure that can be applied to arguments
  given: "bar"
  arguments...: [none]

The reason we can simply drop ◊foo into the text argument of another Pollen command is that the variable 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:

#lang pollen
(define zam 42)
The value of zam is zam

The value of zam is 42

In an unsaved DrRacket file, or a file without a special Pollen source extension, the #lang pollen designation invokes the Pollen preprocessor by default. You can explicitly invoke preprocessor mode by starting a file with #lang pollen/pre. See also Preprocessor (.pp extension).

If the variable holds a container datatype (like a list, hash, or vector), Pollen will produce the Racket text representation of the item. Here, zam is a list of integers:

#lang pollen
(define zam (list 1 2 3))
The value of zam is zam

The value of zam is '(1 2 3)

This feature is included for your convenience. But in general, your readers won’t want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using string-join from the racket/string library:

#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 1 and 2 and 3

Pollen will still produce an error if you try to convert an esoteric value to a string. Here, zam is the addition function (+):

#lang pollen
(define zam +)
The value of zam is zam

Pollen decoder: can't convert #<procedure:+> to string

Moreover, Pollen will not perform 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 pollen/markup:

#lang pollen/markup
(define zam (list 1 2 3))
The value of zam is zam

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)

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 edge next to the string px:

#lang pollen
(define edge 100)
p { margin-left: edgepx; }

Pollen decoder: can't convert #<procedure:...t/pollen/tag.rkt:6:2> to string

The example fails because Pollen reads the whole string after the as the single variable name edgepx. Since 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.

In these situations, surround the variable name with vertical bars ◊|like so| to explicitly indicate where the variable name ends. The bars are not treated as part of the name, nor are they included in the result. Once we do that, we get what we intended:

#lang pollen
(define edge 100)
p { margin-left: ◊|edge|px; }

p { margin-left: 100px; }

If you use this notation when you don’t need to, nothing bad will happen. The vertical bars are always ignored.

#lang pollen
(define edge 100)
The value of edge is ◊|edge| pixels}

The value of edge is 100 pixels

10.3.1.4 Inserting metas

Metas are key–value pairs embedded in a source file that are not included in the main output when the source is run, and collected into a separate hash table.

Metas are not a foundational abstraction. They’re just a convenience — a place to store arbitrary pieces of information that you might want to use later.

Pollen occasionally relies on metas. For instance, the get-template-for function will look in the metas of a source file to see if a template is explicitly specified. The pollen/template module also contains functions for working with metas, such as select-from-metas.

To insert a meta, use the standard command syntax for inserting a tag with an attribute pair, but use the special meta name:

#lang pollen
 
some-tag['key: "value"]{Normal tag}
meta['dog: "Roxy"]
some-tag['key: "value"]{Another normal tag}

When you mark a meta like this, two things happen. First, when you run the file, the meta is removed from the result:

'(some-tag ((key "value")) "Normal tag")

'(some-tag ((key "value")) "Another normal tag")

Second, the meta is collected into a hash table that is exported with the name metas. To see this hash table, run the file above in DrRacket, then move to the interactions window and type metas at the prompt:

> metas

'#hash((here-path . "unsaved-editor167056") (dog . "Roxy"))

The only key that’s automatically defined in every meta table is here-path, which is the absolute path to the source file. (Here, because the file hasn’t been saved, you’ll see the unsaved-editor... name instead.)

Still, you can override this too:

#lang pollen
 
some-tag['key: "value"]{Normal tag}
meta['dog: "Roxy"]
some-tag['key: "value"]{Another normal tag}
meta['here-path: "nowhere"]

When you run this code, the result will be the same as before, but this time the metas will be different:

> metas

'#hash((dog . "Roxy") (here-path . "nowhere"))

It doesn’t matter how many metas you put in a source file or where you put them. They’ll all be extracted and put into the 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:

#lang pollen
 
some-tag['key: "value"]{Normal tag}
meta['dog: "Roxy"]
some-tag['key: "value"]{Another normal tag}
meta['dog: "Lex"]

Though there are two metas named 'dog, only the second one persists:

> metas

'#hash((dog . "Lex") (here-path . "unsaved-editor167056"))

10.3.1.5 Inserting a comment

Two options.

To comment out the rest of a single line, use a lozenge followed by a semicolon ◊;.

#lang pollen
span{This is not a comment}
span{Nor is this} ;span{But this is}

'(span "This is not a comment")
'(span "Nor is this")

To comment out a multiline block, use the lozenge–semicolon signal ◊; with curly braces, ◊;{like so}.

#lang pollen
;{
◊span{This is not a comment}
◊span{Nor is this} ;span{But this is}
}
Actually, it's all a comment now

Actually, it's all a comment now

10.3.2 The Racket arguments

The middle part of a text-mode Pollen command contains the Racket arguments [between square brackets.] 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:

#lang pollen
title{The Beginning of the End}

'(title "The Beginning of the End")

But what if you wanted to add attributes to this tag, so that it comes out like this?

'(title ((class "red")(id "first")) "The Beginning of the End")

You can do it with Racket arguments.

Here’s the hard way. You can type out your list of attributes in Racket format and drop them into the brackets as a single argument:

#lang pollen
title['((class "red")(id "first"))]{The Beginning of the End}

'(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 symbol / string pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark ' and end with a colon :. So taken together, they look like this:

#lang pollen
title['class: "red" 'id: "first"]{The Beginning of the End}

'(title ((class "red") (id "first")) "The Beginning of the End")

Racket arguments can be any valid Racket expressions. For instance, this will also work:

#lang pollen
title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End}

'(title ((class "42") (id "first")) "The Beginning of the End")

Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we’ll define a variable called name and use it in the Racket arguments of title:

#lang pollen
(define name "Brennan")
title['class: "red" 'id: name]{The Beginning of the End}

'(title ((class "read") (id "Brennan")) "The Beginning of the End")

You can also use this area for keyword arguments. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the h1 ... h6 tags, you want to consolidate them into one command called heading and select the level separately. You can do this with a keyword, in this case #:level, which is passed as a Racket argument:

#lang pollen
(define (heading #:level which text)
   `(,(string->symbol (format "h~a" which)) ,text))
 
heading[#:level 1]{Major league}
heading[#:level 2]{Minor league}
heading[#:level 6]{Trivial league}

'(h1 "Major league")
'(h2 "Minor league")
'(h6 "Trivial league")

10.3.3 The text argument

The third part of a text-mode Pollen command is the text argument. The text argument {appears between curly braces}. 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.

#lang pollen
div{Do it again. div{And again. div{And yet again.}}}

'(div "Do it again. " (div "And again. " (div "And yet again.")))

Three small details to know about the text argument.

First, the only character that needs special handling in a text argument is the lozenge . 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 ◊"◊".

#lang pollen
definition{This is the lozenge: "◊"}

'(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:

#lang pollen
div{
Roomy!
 
I agree.
}

'(div "Roomy!" "\n" "\n" "I agree.")

Yields the same result as this one:

#lang pollen
div{Roomy!
 
I agree.}

'(div "Roomy!" "\n" "\n" "I agree.")

For the long version, please see [future link: Spaces, Newlines, and Indentation].

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:

'(div "Roomy!" "\n" "\n" "I agree.")

Instead of this:

'(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 jejune function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:

#lang pollen
(define (jejune text)
   `(jejune ,text))
jejune{Irrational confidence}

'(jejune "Irrational confidence")

But watch what happens with a multiline text argument:

#lang pollen
(define (jejune text)
   `(jejune ,text))
jejune{Deeply
        chastened}

jejune: arity mismatch;
the expected number of arguments does not match the given number
  expected: 1
  given: 3
  arguments...:
   "Deeply"
   "\n"
   "chastened"

The answer is to use a rest argument in the function, which takes the “rest” of the arguments — however many there may be — and combines them into a single list. If we rewrite jejune with a rest argument, we can fix the problem:

#lang pollen
(define (jejune . texts)
   `(jejune ,@texts))
jejune{Deeply
        chastened}

'(jejune "Deeply" "\n" "chastened")

10.4 Further reading

The Pollen language is a variant of Racket’s own text-processing language, called Scribble. So many things that are true about Scribble are also true about Pollen. For the sake of clarity & brevity, I’ve omitted them from this summary. But if you want the full story, see @ Syntax in the Scribble documentation.

 
\ No newline at end of file diff --git a/scribblings/command.scrbl b/scribblings/command.scrbl index 5ec8425..0f9d669 100644 --- a/scribblings/command.scrbl +++ b/scribblings/command.scrbl @@ -1,7 +1,7 @@ #lang scribble/manual -@(require scribble/bnf scribble/eval "utils.rkt" +@(require scribble/bnf scribble/eval "utils.rkt" "mb-tools.rkt" (for-syntax racket/base) - (for-label pollen/world (only-in scribble/reader + (for-label pollen/world pollen/render pollen/template (only-in scribble/reader use-at-readtable))) @(define read-eval (make-base-eval)) @@ -24,9 +24,9 @@ I chose the lozenge as the command marker because a) it appears in almost every Here's how you type it: @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 Still, if you don't want to use the lozenge as your command marker, you can use something else. Set Pollen's @racket[world:command-marker] value to whatever character you want. @@ -65,23 +65,23 @@ Each of the three parts is optional. You can also nest commands within each othe Here are a few examples of correct text-mode commands: -@codeblock|{ +@codeblock{ #lang pollen ◊variable-name ◊tag{Text inside the tag.} ◊tag['attr: "value"]{Text inside the tag} ◊get-customer-id["Brennan Huff"] ◊tag{His ID is ◊get-customer-id["Brennan Huff"].} -}| +} And some incorrect examples: -@codeblock|{ +@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}['attr: "value"] ; wrong order -}| +} The next section describes each of these parts in detail. @@ -89,23 +89,23 @@ The next section describes each of these parts in detail. 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: -@codeblock|{ +@codeblock{ #lang racket (define song "Revolution") (format "~a #~a" song (* 3 3)) -}| +} Can be converted to Pollen like so: -@codeblock|{ +@codeblock{ #lang pollen ◊(define song "Revolution") ◊(format "~a #~a" song (* 3 3)) -}| +} And in DrRacket, they produce the same output: -@nested[#:style 'inset]{@racketoutput{Revolution #9}} +@repl-output{Revolution #9} 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. @@ -114,33 +114,36 @@ Beyond that, there's not much to say about Racket mode — any valid expression 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 text mode to Racket mode. So a text-mode command that looks like this: -@racketblock[ - ◊headline[#:size 'enormous]{Man Bites Dog!} -] +@codeblock[#:keep-lang-line? #f]{ +#lang pollen +◊headline[#:size 'enormous]{Man Bites Dog!} +} + Is actually being turned into a Racket-mode command like this: -@racketblock[ - (headline #:size 'enormous "Man Bites Dog!") -] +@codeblock[#:keep-lang-line? #f]{ +#lang racket +(headline #:size 'enormous "Man Bites Dog!") +} Thus a text-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.) 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: -@codeblock|{ - #lang pollen - ◊(define song "Revolution") - ◊(format "~a #~a" song (* 3 3)) -}| +@codeblock{ +#lang pollen +◊(define song "Revolution") +◊(format "~a #~a" song (* 3 3)) +} Can be rewritten using text mode: -@codeblock|{ - #lang pollen - ◊define[song]{Revolution} - ◊format["~a #~a" song (* 3 3)] -}| +@codeblock{ +#lang pollen +◊define[song]{Revolution} +◊format["~a #~a" song (* 3 3)] +} And it will work the same way. @@ -154,44 +157,47 @@ In Pollen, you'll typically use the command name for one of four purposes: @item{To invoke a tag function.} @item{To invoke another function.} @item{To insert the value of a variable.} +@item{To insert a @tt{meta} value.} @item{To insert a comment.} ] @;-------------------------------------------------------------------- @subsubsection{Invoking tag functions} -By default, Pollen treats every command name as a @italic{tag function}. As the name implies, a 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 tagged X-expression with the command name as the tag, and the text argument as the content. -@codeblock|{ - #lang pollen - ◊strong{Fancy Sauce, $1} -}| +@codeblock{ +#lang pollen +◊strong{Fancy Sauce, $1} +} -@racketoutput{@literal{'(strong "Fancy Sauce, $1")}} +@repl-output{'(strong "Fancy Sauce, $1")} -To streamline markup, Pollen doesn't restrict you to a certain set of tags, nor does it make you define your tag functions ahead of time. Just type a tag, and you can start using it. +To streamline markup, Pollen doesn't restrict you to a certain set of tags, nor does it make you define your tags ahead of time. Just type a tag, and you can start using it. -@codeblock|{ +@codeblock{ #lang pollen ◊utterlyridiculoustagname{Oh really?} -}| -@racketoutput{@literal{'(utterlyridiculoustagname "Oh really?")}} +} +@repl-output{'(utterlyridiculoustagname "Oh really?")} -The one restriction is that you can't invent names for tag functions that are already being used for other commands. For instance, @tt{map} is a name permanently reserved by the Racket function @racket[map]. It's also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other @racket[map]: +The one restriction is that you can't invent names for tags that are already being used for other commands. For instance, @tt{map} is a name permanently reserved by the Racket function @racket[map]. It's also a rarely-used HTML tag. But gosh, you really want to use it. Problem is, if you invoke it directly, Pollen will think you mean the other @racket[map]: -@codeblock|{ - #lang pollen - ◊map{Fancy Sauce, $1} -}| -@racketerror{map: arity mismatch;@(linebreak) -the expected number of arguments does not match the given number@(linebreak) -  given: 1@(linebreak) -  arguments...:@(linebreak) +@codeblock{ +#lang pollen +◊map{Fancy Sauce, $1} +} + +@errorblock{ +map: arity mismatch; +the expected number of arguments does not match the given number +  given: 1 +  arguments...:     "Fancy Sauce, $1"} What to do? Read on. @@ -199,42 +205,42 @@ What to do? Read on. @;-------------------------------------------------------------------- @subsubsection{Invoking other functions} -Though every command name starts out as a tag function, it doesn't necessarily end there. You have two options for invoking other functions: defining your own , or invoking others from Racket. +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. @bold{Defining your own functions} Use the @racket[define] command to create your own function for a command name. After that, when you use the command name, you'll get the new behavior. For instance, recall this example showing the default tag-function behavior: -@codeblock|{ - #lang pollen - ◊strong{Fancy Sauce, $1} -}| +@codeblock{ +#lang pollen +◊strong{Fancy Sauce, $1} +} -@racketoutput{@literal{'(strong "Fancy Sauce, $1")}} +@repl-output{'(strong "Fancy Sauce, $1")} We can define @tt{strong} to do something else, like add to the text: -@codeblock|{ - #lang pollen - ◊(define (strong text) `(strong ,(format "Hey! Listen up! ~a" text))) - ◊strong{Fancy Sauce, $1} -}| +@codeblock{ +#lang pollen +◊(define (strong text) `(strong ,(format "Hey! Listen up! ~a" text))) +◊strong{Fancy Sauce, $1} +} -@racketoutput{@literal{'(strong "Hey! Listen up! Fancy Sauce, $1")}} +@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 @tt{strong} is going to get a text argument that it's not defined to handle: -@codeblock|{ - #lang pollen - ◊(define (strong) '(fib "1 1 2 3 5 8 13 ...")) - ◊strong{Fancy Sauce, $1} -}| +@codeblock{ +#lang pollen +◊(define (strong) '(fib "1 1 2 3 5 8 13 ...")) +◊strong{Fancy Sauce, $1} +} -@racketerror{strong: arity mismatch;@(linebreak) -the expected number of arguments does not match the given number@(linebreak) -  expected: 0@(linebreak) -  given: 1@(linebreak) -  arguments...:@(linebreak) +@errorblock{strong: arity mismatch; +the expected number of arguments does not match the given number +  expected: 0 +  given: 1 +  arguments...:     "Fancy Sauce, $1"} Whereas in this version, @tt{strong} accepts an argument called @tt{text}, but then ignores it: @@ -245,7 +251,7 @@ Whereas in this version, @tt{strong} accepts an argument called @tt{text}, but t ◊strong{Fancy Sauce, $1} }| -@racketoutput{@literal{'(fib "1 1 2 3 5 8 13 ...")}} +@repl-output{'(fib "1 1 2 3 5 8 13 ...")} 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. @@ -259,9 +265,9 @@ You aren't limited to functions you define. Any function from Racket, or any Rac ◊range[1 20] }| -@racketoutput{@literal{'(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 @tt{range} as a tag function (and if we had been, then @racketoutput{@literal{'(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 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 @tt{range} as a tag function (and if we had been, then @repl-output{'(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: @@ -271,7 +277,7 @@ We fix this by using the @racket[require] command to bring in the @racketmodname ◊range[1 20] }| -@racketoutput{@literal{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}} +@repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)} Of course, you can also invoke Racket functions indirectly, by attaching them to functions you define for command names: @@ -282,7 +288,7 @@ Of course, you can also invoke Racket functions indirectly, by attaching them to ◊rick[1 20] }| -@racketoutput{@literal{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}} +@repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)} 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. @@ -294,7 +300,7 @@ For instance, suppose we want to use @tt{map} as a tag even though Racket is usi ◊my-map{How I would love this to be a map.} }| -@racketoutput{@literal{'(my-map "How I would love this to be a map.")}} +@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 @tt{map}. We can do this with the Pollen helper @racket[make-default-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too: @@ -307,10 +313,12 @@ But @code{my-map} is not the tag we want. We need to define @code{my-map} to be ◊my-map{How I would love this to be a map.} }| -@racketoutput{@literal{'(map "How I would love this to be a map.")}} +@repl-output{'(map "How I would love this to be a map.")} Problem solved. + + @;-------------------------------------------------------------------- @subsubsection{Inserting the value of a variable} @@ -322,7 +330,7 @@ A Pollen command name usually refers to a function, but it can also refer to a @ The value of foo is ◊foo }| -@racketoutput{@literal{The value of foo is bar}} +@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: @@ -337,13 +345,15 @@ The value of foo is ◊foo[] }| -@racketerror{application: not a procedure;@(linebreak) -expected a procedure that can be applied to arguments@(linebreak) -  given: "bar"@(linebreak) +@errorblock{application: not a procedure; +expected a procedure that can be applied to arguments +  given: "bar"   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). When appropriate, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted: +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). + +In preprocessor source files, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted: @codeblock|{ #lang pollen @@ -351,7 +361,10 @@ The reason we can simply drop @code{◊foo} into the text argument of another Po The value of zam is ◊zam }| -@racketoutput{@literal{The value of zam is 42}} +@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_" + #:doc '(lib "pollen/scribblings/pollen.scrbl")].} 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, @tt{zam} is a @racket[list] of integers: @@ -361,9 +374,9 @@ If the variable holds a container datatype (like a @racket[list], @racket[hash], The value of zam is ◊zam }| -@racketoutput{@literal{The value of zam is '(1 2 3)}} +@repl-output{The value of zam is '(1 2 3)} -This feature is included for your convenience as an author. But in general, your readers won't want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using @racket[string-join] from the @racketmodname[racket/string] library: +This feature is included for your convenience. But in general, your readers won't want to see the Racket representation of a container. So in these cases, you should convert to a string manually in some sensible way. Here, the integers in the list are converted to strings, which are then combined using @racket[string-join] from the @racketmodname[racket/string] library: @codeblock|{ #lang pollen @@ -372,7 +385,7 @@ This feature is included for your convenience as an author. But in general, your The value of zam is ◊string-join[(map number->string zam)]{ and } }| -@racketoutput{@literal{The value of zam is 1 and 2 and 3}} +@repl-output{The value of zam is 1 and 2 and 3} Pollen will still produce an error if you try to convert an esoteric value to a string. Here, @tt{zam} is the addition function (@racket[+]): @@ -382,8 +395,21 @@ Pollen will still produce an error if you try to convert an esoteric value to a The value of zam is ◊zam }| -@racketerror{Pollen decoder: can't convert # to string} +@errorblock{Pollen decoder: can't convert # 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]: + +@codeblock|{ +#lang pollen/markup +◊(define zam (list 1 2 3)) +The value of zam is ◊zam +}| + +This time, the file will produce an error: + +@errorblock{ + 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) +} 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. @@ -395,7 +421,7 @@ For instance, suppose we want to use a variable @tt{edge} next to the string @t p { margin-left: ◊edgepx; } }| -@racketerror{Pollen decoder: can't convert # to string} +@errorblock{Pollen decoder: can't convert # to string} The example fails because Pollen reads the whole string after the @litchar{◊} as the single variable name @tt{edgepx}. Since @tt{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. @@ -407,7 +433,7 @@ In these situations, surround the variable name with vertical bars @litchar{◊| p { margin-left: ◊|edge|px; } }| -@racketoutput{p { margin-left: 100px; }} +@repl-output{p { margin-left: 100px; }} If you use this notation when you don't need to, nothing bad will happen. The vertical bars are always ignored. @@ -417,7 +443,82 @@ If you use this notation when you don't need to, nothing bad will happen. The ve The value of edge is ◊|edge| pixels} }| -@racketoutput{The value of edge is 100 pixels} +@repl-output{The value of edge is 100 pixels} + + + + +@;-------------------------------------------------------------------- +@subsubsection{Inserting metas} + +@italic{Metas} are key–value pairs embedded in a source file that are not included in the main output when the source is run, and collected into a separate hash table. + +Metas are not a foundational abstraction. They're just a convenience — a place to store arbitrary pieces of information that you might want to use later. + +@margin-note{Pollen occasionally relies on metas. 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].} + +To insert a meta, use the standard command syntax for inserting a tag with an attribute pair, but use the special @tt{meta} name: + +@codeblock{ +#lang pollen + +◊some-tag['key: "value"]{Normal tag} +◊meta['dog: "Roxy"] +◊some-tag['key: "value"]{Another normal tag} +} + +When you mark a meta like this, two things happen. First, when you run the file, the meta is removed from the result: + +@repl-output{ +'(some-tag ((key "value")) "Normal tag") + +'(some-tag ((key "value")) "Another normal tag") +} + +Second, the meta is collected into a hash table that is exported with the name @tt{metas}. To see this hash table, run the file above in DrRacket, then move to the interactions window and type @exec{metas} at the prompt: + +@terminal{ +> metas +'#hash((here-path . "unsaved-editor167056") (dog . "Roxy")) +} + +The only key that's automatically defined in every meta table is @tt{here-path}, which is the absolute path to the source file. (Here, because the file hasn't been saved, you'll see the @tt{unsaved-editor...} name instead.) + +Still, you can override this too: + +@codeblock{ +#lang pollen + +◊some-tag['key: "value"]{Normal tag} +◊meta['dog: "Roxy"] +◊some-tag['key: "value"]{Another normal tag} +◊meta['here-path: "nowhere"] +} + +When you run this code, the result will be the same as before, but this time the metas will be different: + +@terminal{ +> metas +'#hash((dog . "Roxy") (here-path . "nowhere")) +} + +It doesn't matter how many metas you put in a source file or where you put them. They'll all be extracted and put into the @tt{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 + +◊some-tag['key: "value"]{Normal tag} +◊meta['dog: "Roxy"] +◊some-tag['key: "value"]{Another normal tag} +◊meta['dog: "Lex"] +} + +Though there are two metas named @racket['dog], only the second one persists: + +@terminal{ +> metas +'#hash((dog . "Lex") (here-path . "unsaved-editor167056")) +} @;-------------------------------------------------------------------- @@ -433,8 +534,8 @@ To comment out the rest of a single line, use a lozenge followed by a semicolon ◊span{Nor is this} ◊;span{But this is} }| -@racketoutput{@literal{'(span "This is not a comment")} @(linebreak) -@literal{'(span "Nor is this")}} +@repl-output{'(span "This is not a comment") +'(span "Nor is this")} To comment out a multiline block, use the lozenge–semicolon signal @litchar{◊;} with curly braces, @litchar{◊;@"{"}like so@litchar{@"}"}. @@ -448,7 +549,7 @@ Actually, it's all a comment now }| -@racketoutput{@literal{Actually, it's all a comment now}} +@repl-output{Actually, it's all a comment now} @;-------------------------------------------------------------------- @subsection{The Racket arguments} @@ -462,11 +563,11 @@ For instance, tag functions. Recall from before that any not-yet-defined command ◊title{The Beginning of the End} }| -@racketoutput{@literal{'(title "The Beginning of the End")}} +@repl-output{'(title "The Beginning of the End")} But what if you wanted to add attributes to this tag, so that it comes out like this? -@racketoutput{@literal{'(title ((class "red")(id "first")) "The Beginning of the End")}} +@repl-output{'(title ((class "red")(id "first")) "The Beginning of the End")} You can do it with Racket arguments. @@ -477,7 +578,7 @@ Here's the hard way. You can type out your list of attributes in Racket format a ◊title['((class "red")(id "first"))]{The Beginning of the End} }| -@racketoutput{@literal{'(title ((class "red") (id "first")) "The Beginning of the End")}} +@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 @racket[symbol] / @racket[string] pairs between the Racket-argument brackets. The only caveat is that the symbols have to begin with a quote mark @litchar{'} and end with a colon @litchar{:}. So taken together, they look like this: @@ -487,7 +588,7 @@ But that's a lot of parentheses to think about. So here's the easy way. Anytime ◊title['class: "red" 'id: "first"]{The Beginning of the End} }| -@racketoutput{@literal{'(title ((class "red") (id "first")) "The Beginning of the End")}} +@repl-output{'(title ((class "red") (id "first")) "The Beginning of the End")} Racket arguments can be any valid Racket expressions. For instance, this will also work: @@ -496,7 +597,7 @@ Racket arguments can be any valid Racket expressions. For instance, this will al ◊title['class: (format "~a" (* 6 7)) 'id: "first"]{The Beginning of the End} }| -@racketoutput{@literal{'(title ((class "42") (id "first")) "The Beginning of the End")}} +@repl-output{'(title ((class "42") (id "first")) "The Beginning of the End")} Since Pollen commands are really just Racket arguments underneath, you can use those too. Here, we'll define a variable called @tt{name} and use it in the Racket arguments of @tt{title}: @@ -506,7 +607,7 @@ Since Pollen commands are really just Racket arguments underneath, you can use t ◊title['class: "red" 'id: ◊name]{The Beginning of the End} }| -@racketoutput{@literal{'(title ((class "read") (id "Brennan")) "The Beginning of the End")}} +@repl-output{'(title ((class "read") (id "Brennan")) "The Beginning of the End")} You can also use this area for @italic{keyword arguments}. Keyword arguments can be used to provide options for a particular Pollen command, to avoid redundancy. Suppose that instead of using the @tt{h1 ... h6} tags, you want to consolidate them into one command called @tt{heading} and select the level separately. You can do this with a keyword, in this case @racket[#:level], which is passed as a Racket argument: @@ -520,9 +621,9 @@ You can also use this area for @italic{keyword arguments}. Keyword arguments can ◊heading[#:level 6]{Trivial league} }| -@racketoutput{@literal{'(h1 "Major league")} @(linebreak) -@literal{'(h2 "Minor league")} @(linebreak) -@literal{'(h6 "Trivial league")} +@repl-output{'(h1 "Major league") +'(h2 "Minor league") +'(h6 "Trivial league") } @;-------------------------------------------------------------------- @@ -535,7 +636,7 @@ The third part of a text-mode Pollen command is the text argument. The text argu ◊div{Do it again. ◊div{And again. ◊div{And yet again.}}} }| -@racketoutput{@literal{'(div "Do it again. " (div "And again. " (div "And yet again.")))}} +@repl-output{'(div "Do it again. " (div "And again. " (div "And yet again.")))} Three small details to know about the text argument. @@ -546,7 +647,7 @@ First, the only character that needs special handling in a text argument is the ◊definition{This is the lozenge: ◊"◊"} }| -@racketoutput{@literal{'(definition "This is the lozenge: ◊")}} +@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: @@ -559,7 +660,7 @@ I agree. } }| -@racketoutput{@literal{'(div "Roomy!" "\n" "\n" "I agree.")}} +@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")} Yields the same result as this one: @@ -570,18 +671,18 @@ Yields the same result as this one: I agree.} }| -@racketoutput{@literal{'(div "Roomy!" "\n" "\n" "I agree.")}} +@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")} For the long version, please see [future link: Spaces, Newlines, and Indentation]. 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: -@racketoutput{@literal{'(div "Roomy!" "\n" "\n" "I agree.")}} +@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")} Instead of this: -@racketoutput{@literal{'(div "Roomy!\n\nI agree.")}} +@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 @tt{jejune} function only accepts a single argument. It will work with a single-line text argument, because that produces a single string: @@ -592,7 +693,7 @@ Under most circumstances, these two tagged X-expressions will behave the same wa ◊jejune{Irrational confidence} }| -@racketoutput{@literal{'(jejune "Irrational confidence")}} +@repl-output{'(jejune "Irrational confidence")} But watch what happens with a multiline text argument: @@ -604,13 +705,13 @@ But watch what happens with a multiline text argument: chastened} }| -@racketerror{jejune: arity mismatch;@(linebreak) -the expected number of arguments does not match the given number@(linebreak) -  expected: 1@(linebreak) -  given: 3@(linebreak) -  arguments...:@(linebreak) -   "Deeply"@(linebreak) -   "\n"@(linebreak) +@errorblock{jejune: arity mismatch; +the expected number of arguments does not match the given number +  expected: 1 +  given: 3 +  arguments...: +   "Deeply" +   "\n"    "chastened"} The answer is to use a @italic{rest argument} in the function, which takes the ``rest'' of the arguments — however many there may be — and combines them into a single @racket[list]. If we rewrite @tt{jejune} with a rest argument, we can fix the problem: @@ -623,7 +724,8 @@ The answer is to use a @italic{rest argument} in the function, which takes the ` chastened} }| -@racketoutput{@literal{'(jejune "Deeply" "\n" "chastened")}} +@repl-output{'(jejune "Deeply" "\n" "chastened")} + @section{Further reading}