# Reader macros

Loading Consfigurator defines the `:CONSFIGURATOR` named readtable. Its original purpose was to define a few reader macros to make Lisp more readily usable for Unix systems administration, and as such it’s helpful in consfigs. We now have the broader aim of providing a readtable that renders Lisp more useful for general Unix-style text manipulation. To this end, the reader macros we define are all inspired by Perl syntax.

## Backwards compatibility

We don’t expect to make incompatible changes to how these reader macros work, except to make them work more like their Perl equivalents. With this in mind, some particular reservations are made for particular macros, as detailed below.

## `#?`: Regexps & interpolation

Sharp-question mark is the well-known CL-INTERPOL reader macro.

## `#~m//`: PCRE matching

This provides an abbreviation for shell- and Perl-style regexp matching:

    (#~m/b.+b/i "FooBarBaz") => "BarB"
    (#~m/b(.+)b/i "FooBarBaz") => #("ar")
    (mapcar #3~m/(\w+)(\W+)(\w+)/ '("one two" "three four" "five six"))
        => ("two" "four" "six")

Any delimiters supported by `CL-INTERPOL` may be used, and the `m` is always optional. Standard trailing options `g`, `i`, `m`, `s` and `x` are meaningful. There is also `p`, which means to attempt to parse the matched strings and substrings as numbers; if a substring cannot be parsed as a number, it is returned unmodified.

The return value depends on the numeric argument before the tilde:

- `#~m//`, with no argument, returns a vector of the substrings corresponding to the capture groups, or if there were no capture groups, just the whole matched string.

- `#0~m//` returns two values: the whole matched string, and a vector of capture group substrings. (This is plain `CL-PPCRE:SCAN-TO-STRINGS`.)

- `#n~m//` returns two values: the nth capture group’s substring, and a vector of all the capture group substrings.

## `#!~m//`: PCRE negative matching

Equivalent to `(not #~m//)`.

## `#~s///`: PCRE substitution

This provides an abbreviation for shell- and Perl-style regexp substitution:

    (#~s/foo/bar/ "foobarbaz") => "foofoobaz"
    (mapcar #~s/:.+:/`\&`/ '(":Hello:" ":Goodbye:")) => ("`:Hello:`" "`:Goodbye:`")

Again, any delimiters supported by `CL-INTERPOL` may be used, and the same trailing options, except for `p`, are meaningful. This is `CL-PPCRE:REGEX-REPLACE` or `CL-PPCRE:REGEX-REPLACE-ALL`, which see regarding return values.

## `#>EOF>` and `#>>EOF>>`: Heredocs

Following `#>EOF>`, all characters are read into a string until the next literal `EOF`. You may use any string in place of `EOF`, except that it must not begin with a tilde or contain any whitespace, and for the sake of future extension, it must not begin with a backwards slash or begin or end with single or double quotation marks.

You can double up the `>`, as in `#>>EOF>>`, to skip the remainder of the line on which the `#>>EOF>>` appears, starting the heredoc at the beginning of the following line. For the sake of future extension, the remainder of the line after the `#>>EOF>>` must not contain anything other than a single-line comment.

The specification of the terminating string may be preceded by a tilde, as in `#>>~EOF>>`, to mean an indented heredoc:

    (foo "argument 1" #>>~EOF>>
         My line 1.
         My line 2.
         EOF)

The function receives `"My line 1.\nMy line 2.\n"`.

## See also

- perlop(1)

- Let Over Lambda ch. 4, which originally inspired `#~m//` and `#~s///`.