On this page:
2.1 Invoking mzpp
2.2 mzpp files
2.3 Raw preprocessing directives
2.4 The mzpp read-eval-print loop
2.5 Provided bindings
preprocess
skip-to
debug?
no-spaces?
beg-mark
end-mark
push-indentation
pop-indentation
show
newline*
include
stdin
stdout
stderr
cd
current-file
thunk

2 mzpp

mzpp is a simple preprocessor that allows mixing Racket code with text files in a similar way to PHP or BRL. Processing of input files works by translating the input file to Racket code that prints the contents, except for marked portions that contain Racket code. The Racket parts of a file are marked with << and >> tokens by default. The Racket code is then passed through a read-eval-print loop that is similar to a normal REPL with a few differences in how values are printed.

2.1 Invoking mzpp

Use the --h flag to get the available flags. See above for an explanation of the --run flag.

2.2 mzpp files

Here is a sample file that mzpp can process, using the default beginning and ending markers:

  << (define bar "BAR") >>

  foo1

  foo2 << bar newline* bar >> baz

  foo3

First, this file is converted to the following Racket code:

  (thunk (cd "tmp/") (current-file "foo"))

  (thunk (push-indentation ""))

   (define bar "BAR") (thunk (pop-indentation))

  newline*

  "foo1"

  newline*

  "foo2 "

  (thunk (push-indentation "     "))

   bar newline* bar (thunk (pop-indentation))

  " baz"

  newline*

  "foo3"

  newline*

  (thunk (cd "/home/eli") (current-file #f))

which is then fed to the REPL, resulting in the following output:

  foo1

  foo2 BAR

       BAR baz

  foo3

To see the processed input that the REPL receives, use the --debug flag. Note that the processed code contains expressions that have no side-effects, only values—see below for an explanation of the REPL printing behavior. Some expressions produce values that change the REPL environment, for example, the indentation commands are used to keep track of the column where the Racket marker was found, and cd is used to switch to the directory where the file is (here it was in "/home/foo/tmp") so including a relative file works. Also, note that the first newline* did not generate a newline, and that the one in the embedded Racket code added the appropriate spaces for indentation.

It is possible to temporarily switch from Racket to text-mode and back in a way that does not respect a complete Racket expression, but you should be aware that text is converted to a sequence of side-effect free expressions (not to a single string, and not expression that uses side effects). For example:

  << (if (zero? (random 2))

       (list >>foo1<<)

       (list >>foo2<<))

  >>

  << (if (zero? (random 2)) (list >>

  foo1

  <<) (list >>

  foo2

  <<)) >>

will print two lines, each containing foo1 or foo (the first approach plays better with the smart space handling). The show function can be used instead of list with the same results, since it will print out the values in the same way the REPL does. The conversion process does not transform every continuous piece of text into a single Racket string because doing this:

(Note that this is different from the BRL approach.)

2.3 Raw preprocessing directives

Some preprocessing directives happen at the "raw level"—the stage where text is transformed into Racket expressions. These directives cannot be changed from within transformed text because they change the way this transformation happens. Some of these transformation

2.4 The mzpp read-eval-print loop

The REPL is initialized by requiring preprocessor/mzpp, so the same module provides both the preprocessor functionality as well as bindings for embedded Racket code in processed files. The REPL is then fed the transformed Racket code that is generated from the source text (the same code that --debug shows). Each expression is evaluated and its result is printed using the show function (multiple values are all printed), where show works in the following way:

2.5 Provided bindings

 (require preprocessor/mzpp)

First, bindings that are mainly useful for invoking the preprocessor:

procedure

(preprocess in ...)  void?

  in : (or/c path-string? input-port?)
This is the main entry point to the preprocessor—invoking it on the given list of files and input ports. This is quite similar to include, but it adds some setup of the preprocessed code environment (like requiring the mzpp module).

parameter

(skip-to)  string?

(skip-to str)  void?
  str : string?
A string parameter—when the preprocessor is started, it ignores everything until a line that contains exactly this string is encountered. This is primarily useful through a command-line flag for scripts that extract some text from their own body.

parameter

(debug?)  boolean?

(debug? on?)  void?
  on? : any/c
A boolean parameter. If true, then the REPL is not invoked, instead, the converted Racket code is printed as is.

parameter

(no-spaces?)  boolean?

(no-spaces? on?)  void?
  on? : any/c
A boolean parameter. If true, then the "smart" preprocessing of spaces is turned off.

parameter

(beg-mark)  string?

(beg-mark str)  void?
  str : string?

parameter

(end-mark)  string?

(end-mark str)  void?
  str : string?
These two parameters are used to specify the Racket beginning and end markers.

All of the above are accessible in preprocessed texts, but the only one that might make any sense to use is preprocess and include is a better choice. When include is used, it can be wrapped with parameter settings, which is why they are available. Note in particular that these parameters change the way that the text transformation works and have no effect over the current preprocessed document (for example, the Racket marks are used in a different thread, and skip-to cannot be re-set when processing has already began). The only one that could be used is no-spaces? but even that makes little sense on selected parts.

The following are bindings that are used in preprocessed texts:

procedure

(push-indentation str)  void?

  str : string?

procedure

(pop-indentation)  void?

These two calls are used to save the indentation column where the Racket beginning mark was found, and will be used by newline* (unless smart space handling mode is disabled).

procedure

(show v)  void?

  v : any/c
The arguments are displayed as specified above.

procedure

(newline*)  void?

This is similar to newline except that it tries to handle spaces in a “smart” way—it will print a newline and then spaces to reach the left margin of the opening <<. (Actually, it tries a bit more, for example, it won’t print the spaces if nothing is printed before another newline.) Setting no-spaces? to true disable this leaving it equivalent to newline.

procedure

(include file ...)  void?

  file : path-string?
This is the preferred way of including another file in the processing. File names are searched relatively to the current preprocessed file, and during processing the current directory is temporarily changed to make this work. In addition to file names, the arguments can be input ports (the current directory is not changed in this case). The files that will be incorporated can use any current Racket bindings etc, and will use the current markers—but the included files cannot change any of the parameter settings for the current processing (specifically, the marks and the working directory will be restored when the included files are processed).

Note that when a sequence of files are processed (through command-line arguments or through a single include expression), then they are all taken as one textual unit—so changes to the markers, working directory etc in one file can modify the way sequential files are processed. This means that including two files in a single include expression can be different than using two expressions.

value

stdin : parameter?

value

stdout : parameter?

value

stderr : parameter?

value

cd : parameter?

These are shorter names for the corresponding port parameters and current-directory.

parameter

(current-file)  path-string?

(current-file path)  void?
  path : path-string?
This is a parameter that holds the name of the currently processed file, or #f if none.

syntax

(thunk expr ...)

Expands to (lambda () expr ...).