Pinaret Reference Manual

Version 0.9

1. Introduction

Pinaret is a static website generator. It's based on a file format for defining text blocks, which is integrated with a language that serves both to interpolate and transform strings, and to script the generation of variants. Features:

Installation

These instructions assume you've downloaded and unzipped pinaret-0.9.zip.

  1. Make sure you have Racket installed (you can choose either Racket or Minimal Racket), and that the directory containing the Racket programs is included in the PATH environment variable.
  2. Move the pinaret-0.9 directory to a permanent location.
  3. Change to the pinaret subdirectory of the pinaret-0.9 directory and run the command:
    raco pkg install
  4. Make sure that the directory where the previous command installed the Pinaret launchers is included in the PATH environment variable.

Maintenance

Pinaret is maintained and was developed by Josep Portella Florit. Future updates will be distributed through Pinaret's home page. Backwards compatibility is not guaranteed at this stage.

License

Copyright © 2016 Josep Portella Florit

Pinaret (software and manual) is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program in a file named LICENSE.html. If not, see the GNU website.

2. Dictionary Files

Pinaret uses dictionary files, which are UTF-8 encoded text files with the .dict extension, and contain zero or more key/value pairs.

A key/value pair starts with a | character at the beginning of a line, but the | character may be omitted on the first key/value pair. The key starts with the first character that is neither whitespace nor |, and ends at the first whitespace character. The next character is part of the value, which continues until a | character is found at the beginning of a line or until the file ends. This is an example with two key/value pairs:

|single This is a single line value
|multi
This is a
multi-line
value

At the beginning of a line inside a value, the \ character is discarded if it's followed by the | or \ characters.

Different key/value pairs in the same dictionary file can't have the same key, except if the key is #; any key/value pairs with # as the key are ignored.

Values are procedures that can be called by its key and return a string. Values may have the result of procedure calls interpolated. A interpolation starts with the $ character, followed by the procedure name enclosed within { and }. Example:

|who World
|message Hello ${who}

In this example, calling the procedure message will return the string Hello World.

Whitespace characters between {, the procedure name, and } are ignored. If a value must contain ${ literally, you can escape it as $\{, which in turn can be escaped as $\\{ and so on.

There are predefined procedures, like random, which returns the string of a possibly different random number on each invocation. Procedures can receive arguments, which is mostly useful for calling predefined procedures, but procedures defined in dictionary files can receive arguments too, however in that case the arguments don't correspond to any formal parameters, but are accessed through special procedures named local and locals. To pass an argument to a procedure, it must be separated from the procedure name or other arguments by whitespace characters, for example, ${A B C} calls the A procedure with B and C as arguments.

If some special character, like whitespace characters, or the procedure application characters ({}), must be part of an argument or a procedure name, you must escape it with the \ character, which in turn must also be escaped by itself to avoid its special meaning. Alternatively, by enclosing a sequence of characters in quotes (', " or `) you can disable the special treatment of the whitespace characters within the quotes. The same quote character used to mark the beginning must be used to mark the end. Quotes must also be escaped with the \ character to prevent their special meaning, but within quotes you only need to escape the character that was used to start quoting. For example, ${A B' "C" D'} is equivalent to ${A "B \"C\" "D}; both call a procedure named A with a single argument B "C" D.

Procedure calls can also be interpolated inside other procedure calls, but the $ character must be omitted for nested procedure calls. For example, ${A{B}C} will concatenate A, the result of calling B, and C, and call the resulting string. If a string returned by a nested procedure call includes a whitespace character or any special character, it won't have any special meaning. Whitespace characters retain their special meaning inside nested procedure calls which are enclosed in quotes, and the same quote character that encloses the call may be used again inside the nested call. For example, ${"{A "B C"}"} is equivalent to ${{A "B C"}}, both will call the procedure A with a single argument B C, and call the procedure named as the resulting string.

The arguments are passed unevaluated to procedures. Most procedures always evaluate their arguments, usually from left to right, however a procedure may not evaluate an argument, or may evaluate it multiple times; this allows a procedure to act as a control structure.

A procedure application that includes a | character after the procedure name or after an argument, is transformed by moving the procedure name and its arguments until the | to a nested procedure application following the first argument after the |, for example, {A B | C D} is transformed to {C {A B} D}. There doesn't need to be any whitespace characters around the | for it to be effective. The special meaning of a | inside a procedure application can be avoided by escaping it with \ or by enclosing it in quotes.

3. Conventions

When a predefined procedure receives a file or directory path, it avoids directory traversal attacks by signaling an error when necessary.

Because evaluated arguments and return values are always strings, the predefined procedures follow a set of conventions when they need to receive or return Booleans, numbers, sequences or regular expressions.

The Boolean true value corresponds to the true string, and the Boolean false value corresponds to the false string. When a predefined procedure expects a Boolean, it signals an error if the provided string isn't a valid Boolean.

Numbers can be expressed like in Racket. Refer to the Racket documentation for more information. When a predefined procedure expects a number, it signals an error if the provided string isn't a valid number.

To process a sequence, a predefined procedure like join or for-each temporarily sets up an element processing coroutine. The coroutine can then be called with the yield procedure; a sequence is the effect of calling yield zero or more times. By convention, a procedure that is documented to produce a sequence, also returns an empty string, although some procedures may not return an empty string, but something more useful; in that case, it's specified in the documentation of the procedure.

Some predefined procedures expect regular expressions. The syntax is the same as Racket's pregexp syntax; refer to the Racket documentation for more information. When a predefined procedure expects a regular expression, it signals an error if the provided string isn't a valid regular expression.

4. Commands

Pinaret includes the commands pinaret-init, pinaret and pinaret-repl. All Pinaret commands accept the following options:

4.1. Command: pinaret-init

With the pinaret-init command you can create and initialize a directory for a new project. You must pass the name of the directory to be created as the only argument, for example:

pinaret-init my-project

4.2. Command: pinaret

With the pinaret command you can generate files according to the files in the input directory of your project. You must run the command in your project's directory, with no arguments or with a combination of the following options:

The files in the input directory (and its subdirectories) are either static files or generators, which are dictionary files. The static files are copied into the output directory (or its corresponding subdirectory) unless it's determined that they should be omitted. By default no files are omitted, but this can be customized by setting the omit-file? parameter in the custom.rkt file. A static file that was already copied into the output directory, won't be copied again until the input file is modified.

The library directory and its subdirectories may contain dictionary files. The procedures defined in those dictionary files extend the set of predefined procedures. Although procedures defined in the library may shadow predefined procedures, a library dictionary file can't redefine a procedure defined in another library dictionary file.

For each generator in the input directory and its subdirectories (or just those specified with the --generator command line option), the procedures defined in them extend the set of library and predefined procedures. Then, if a procedure named input is defined, it's called expecting it to produce a sequence (unless the --input command line option was provided, in which case the option value will be evaluated instead to obtain the sequence). For each element in the resulting sequence (or once, if no procedure named input is defined and --input was not provided), if a procedure named output is defined, it's called to obtain the contents of the output file, otherwise an error is signaled.

If a procedure named path is defined, it's called to obtain the path of the output file; if it isn't defined, the path of the output file is the path of the generator excluding the input directory and the .dict extension. Obviously, when the input procedure or the --input option value produces a sequence with more than one element, you'll want a path procedure that calls the procedures introduced by the sequence, otherwise you'll end up with the same output file being overwritten for each element in the sequence. For example, this generator produces the files 0.txt, 1.txt and 2.txt:

|input ${range i 3}
|path ${i}.txt
|output

The output file path may be either absolute or relative; anyways it'll be appended to the output directory to obtain the final path. If the output file path ends with a directory separator, if a procedure named index-file is defined, it's called to obtain a file name which will be appended to the output path; otherwise an error is signaled. Directory traversal attacks are avoided by signaling an error when necessary.

During the call to the output procedure, the write procedure may be used to write directly to the output file. You'll rarely need to use this procedure, but it's useful if you need to write enormous files that don't fit in RAM. Finally, the string returned by the output procedure will be appended to the output file.

By default, the output file will be encoded using UTF-8. If you define a procedure named bytes? that returns true, the Unicode characters will be encoded using Latin-1, one byte per character, and an error will be signaled if there is a character with a code point greater than 255.

You can set the output file permissions if you define a procedure named mode that returns a number, which may be in octal notation, for example, #o755.

When an error is signaled during file generation, the file generation is halted and the error message is shown. If the --repl-on-error command line option was provided, the read-eval-print-loop prompt is displayed; otherwise, or after the interaction with the REPL is finished, the program either finishes with an exit status of 1 or waits for files to change if the --loop command line option was provided.

Unless the --loop command line option was provided, the program finishes with an exit status of 0 when there are no more files to generate.

With the --loop option, any change to library dictionary files or input files triggers the file generation process. You may want to customize ignore-file? to ignore certain static files.

4.3. Command: pinaret-repl

With the pinaret-repl (read-eval-print-loop) command you can test the procedures defined in your project's library and generators. You must run the command in your project's directory.

When run with no arguments, pinaret-repl shows the documentation of the help procedure (which can be inhibited with the --quiet / -q option), displays a prompt (> ) and waits for input. As each input line is read, it's evaluated, the result is printed and the prompt is shown again.

If a line can't be evaluated because a procedure application isn't finished, a different prompt is displayed (| ). When the next line is read, it's appended to the previous unevaluated line before evaluation is tried.

When an error is signaled during evaluation, the error message is shown, and the prompt is displayed again.

To try the procedures defined in the generator dictionary files, you can use the extern procedure.

If the library or generator dictionary files are modified while running the program, they'll be reloaded automatically.

The program finishes when there is no more input.

5. Procedures

5.1. Documentation

help Procedure

{help P} returns the documentation of the procedure P. Example: ${help procedures}

procedures Procedure

{procedures} returns the list of defined procedures separated by newlines. {procedures P} iterates the list of defined procedures; for each procedure, temporarily defines a procedure named P that returns the name of the current procedure, and calls {yield}. For example, ${procedures} returns the same as: ${procedures p | join {p} {char 10}}

source Procedure

{source P} returns the location of the definition of the procedure P.

version Procedure

{version} returns the Pinaret version.

5.2. Control Flow

case Procedure

{case A K V} returns V if A is the same string as K; otherwise, signals an error. {case A K V D} returns D instead of signaling an error. {case A K1 V1 K2 V2 ...} returns V1 if A is the same string as K1; otherwise, is equivalent to {case A K2 V2 ...}.

if Procedure

{if B C A} returns C if B is true; if it's false, returns A. {if B C} is equivalent to {if B C ''}.

5.3. Logic

and Procedure

{and} returns true. {and A} returns true if A is true; if it's false, returns false. {and A1 A2 ... An} is equivalent to {and A2 ... An} if A1 is true; if it's false, returns false.

not Procedure

{not A} returns false if A is true; if it's false, returns true.

or Procedure

{or} returns false. {or A} returns true if A is true; if it's false, returns false. {or A1 A2 ... An} is equivalent to {or A2 ... An} if A1 is false; if it's true, returns true.

5.4. Comments

# Procedure

{# ...} ignores its arguments and returns an empty string. It may be used to comment code out.

5.5. Procedures

defined? Procedure

{defined? P} returns true if the procedure P is defined, otherwise returns false.

extended Procedure

{extended A P1 V1 ... Pn Vn} temporarily defines the procedures named P1 ... Pn, which return the corresponding and previously evaluated V1 ... Vn, and returns A. Example: ${extended {+ {a} {b}} a 1 b 2}

extended* Procedure

{extended* A P1 V1 ... Pn Vn} temporarily defines the procedures named P1 ... Pn, which return the corresponding V1 ... Vn, and returns A. Example: ${extended* {+ {a} {b}} a 2 b {* {a} 2}}

5.6. Strings

char Procedure

{char A} returns the character with ordinal A. {char A1 A2 ... An} is equivalent to {char A1}{char A2 ... An}. Example: ${char 72 69 76 76 79}

downcase Procedure

{downcase A} returns the downcase conversion of A.

normalize-nfc Procedure

{normalize-nfc A} returns the Unicode normalized form C of A.

normalize-nfd Procedure

{normalize-nfd A} returns the Unicode normalized form D of A.

normalize-nfkc Procedure

{normalize-nfkc A} returns the Unicode normalized form KC of A.

normalize-nfkd Procedure

{normalize-nfkd A} returns the Unicode normalized form KD of A.

ord Procedure

{ord A} returns the integer ordinal of the one-character string A.

pregexp-match? Procedure

{pregexp-match? A R} returns true if A matches the regular expression R; otherwise returns false.

pregexp-replace Procedure

{pregexp-replace A R I} returns A with all the occurrences of the regular expression R replaced by I. {pregexp-replace A P R I} temporarily defines a procedure named P before evaluating I for each match. {P 0} returns the match substring. {P} is equivalent to {P 0}. {P N}, where N is greater than 0, returns the match group number N.

pregexp-replace* Procedure

{pregexp-replace* A R I} returns A with the first occurrence of the regular expression R replaced by I. {pregexp-replace* A P R I} temporarily defines a procedure named P before evaluating I. {P 0} returns the match substring. {P} is equivalent to {P 0}. {P N}, where N is greater than 0, returns the match group number N.

replace Procedure

{replace A F T} returns A with all the occurrences of F replaced by T.

replace* Procedure

{replace* A F T} returns A with the first occurrence of F replaced by T.

string<=? Procedure

{string<=?} and {string<=? A} return true. {string<=? A1 A2 ... An} is equivalent to {string<=? A2 ... An} if A1 precedes or matches A2 in lexicographical order; otherwise, returns false.

string<? Procedure

{string<?} and {string<? A} return true. {string<? A1 A2 ... An} is equivalent to {string<? A2 ... An} if A1 precedes A2 in lexicographical order; otherwise, returns false.

string=? Procedure

{string=?} and {string=? A} return true. {string=? A1 A2 ... An} is equivalent to {string=? A2 ... An} if A1 and A2 are the same string; otherwise, returns false.

string>=? Procedure

{string>=?} and {string>=? A} return true. {string>=? A1 A2 ... An} is equivalent to {string>=? A2 ... An} if A1 goes after or matches A2 in lexicographical order; otherwise, returns false.

string>? Procedure

{string>?} and {string>? A} return true. {string>? A1 A2 ... An} is equivalent to {string>? A2 ... An} if A1 goes after A2 in lexicographical order; otherwise, returns false.

titlecase Procedure

{titlecase A} returns the titlecase conversion of A.

upcase Procedure

{upcase A} returns the upcase conversion of A.

5.7. Sequences and Strings

concat Procedure

{concat A ...} produces a sequence by concatenating the sequences produced by its arguments, and returns the concatenation of the strings returned by its arguments.

drop Procedure

{drop S N} produces a sequence by discarding N elements from the beginning of the sequence S, and returns the string returned by S without the first N characters.

length Procedure

{length A} returns the length of the sequence produced by A, if it's greater than 0; otherwise, returns the length of the string returned by A.

random Procedure

{random} returns a random real number between 0 and 1. {random A} returns a random integer between 0 (included) and A (excluded) if it isn't an empty string, otherwise returns an empty string. It also randomizes the order of the sequence produced by A.

reverse Procedure

{reverse A} reverses both the order of the sequence produced and the string returned by A.

take Procedure

{take S N} produces a sequence by taking N elements from the beginning of the sequence S, and returns the first N characters of the string returned by S.

5.8. Sequences

any? Procedure

{any? S A} returns true if A is true for any element in the sequence S; otherwise, returns false.

apply Procedure

{apply S P T} calls the procedure named P with as many arguments as elements in the sequence S, each one being T for the corresponding element of the sequence. {apply S P A ... T} prepends A ... to the arguments to P. Example: ${range i 10 | apply + -2 -1 {i}}

args Procedure

{args P A ...} for each A, temporarily defines a procedure named P, which returns A, and calls {yield}. Example: ${args x 1 2 3 | join {x} ,}

chars Procedure

{chars A P} iterates the characters in A; for each character, temporarily defines a procedure named P, which returns the character, and calls {yield}.

digest Procedure

{digest S P N F} iterates the sequence S; for each element, temporarily defines a procedure named P and evaluates N. {P P1 V1 ... Pn Vn} temporarily defines the procedures P1 ... Pn with values V1 ... Vn, which will be available in the next evaluation of N or F. Finally, F is returned. {digest S P N F P1 V1 ... Pn Vn} is equivalent to {extended {digest S P N F} P1 V1 ... Pn Vn}. Example: ${range i 10 | digest k {k j {+ {i} {j}}} {j} j 0}

drop-while Procedure

{drop-while S B} produces a sequence by discarding the elements of the beginning of the sequence S while B is true.

every? Procedure

{every? S A} returns true if A is true for every element in the sequence S; otherwise, returns false.

filter Procedure

{filter S B} produces a sequence with the elements of the sequence S in which B is true.

for-each Procedure

{for-each S A} evaluates A for each element of the sequence S. It's useful for conditionally calling {yield}, or calling procedures with side effects. Returns an empty string.

group-by Procedure

{group-by S P A} groups the elements of the sequence S by A; for each group, temporarily defines a procedure named P, which produces a sequence of the elements in the group, and calls {yield}.

join Procedure

{join S A B} collects A for each element of the sequence S, and returns its concatenation separated by B. {join S A} is equivalent to {join S A ''}.

nth Procedure

{nth S N A} returns A for the N-th element of the sequence S.

pregexp-match Procedure

{pregexp-match A P R} iterates the matches of the regular expression R in A; for each match, temporarily defines a procedure named P, and calls {yield}. {P 0} returns the match substring. {P} is equivalent to {P 0}. {P N}, where N is greater than 0, returns the match group number N.

pregexp-split Procedure

{pregexp-split A P R} splits A into substrings by the regular expression R; for each substring, temporarily defines a procedure named P, which returns the substring, and calls {yield}. {pregexp-split A P} is equivalent to {pregexp-split A P '\\s+'}.

range Procedure

{range P A Z S} counts from A to Z, increasing or decreasing by S; for each number, temporarily defines a procedure named P, which returns the current number, and calls {yield}. {range P Z} is equivalent to {range P 0 Z 1}. {range P A Z} is equivalent to {range P A Z 1}.

slice Procedure

{slice S P N} splits the sequence S into subsequences of N elements; for each subsequence, temporarily defines a procedure named P, which produces a sequence of the elements in the subsequence, and calls {yield}.

sort Procedure

{sort S A P} produces a sequence by sorting the sequence S; compares each element by A using the procedure named P, which must accept at least 2 arguments and return true or false. {sort S A} is equivalent to {sort S A string<?}.

split Procedure

{split A P B} splits A into substrings by B; for each substring, temporarily defines a procedure named P, which returns the substring, and calls {yield}. {split A P} is equivalent to {split A P ' '}.

take-while Procedure

{take-while S B} produces a sequence by taking the elements from the beginning of the sequence S while B is true.

with-index Procedure

{with-index S P} iterates the sequence S; for each element, temporarily defines a procedure named P, which returns the index of the element, and calls {yield}.

yield Procedure

{yield} calls the sequence element processing coroutine, which is set up temporarily by procedures like join or for-each. Returns an empty string.

5.9. Date and Time

current-timestamp Procedure

{current-timestamp} returns the current timestamp in the ISO-8601 year-month-day-hour-minute-second-timezone format. The resulting string reflects the time in UTC, but {current-timestamp true} reflects the time according to the local time zone.

5.10. Files

extern Procedure

{extern A G} returns A in the context of the generator with path G, which must be relative to the input directory, and the .dict extension must be omitted. {extern A G P1 V1 ... Pn Vn} is equivalent to {extended {extern A G} P1 V1 ... Pn Vn}.

generator Procedure

{generator} returns the path of the current generator relative to the input directory and without the .dict extension.

generators Procedure

{generators K P D} iterates the generators in the path P (which must be relative to the input directory) limited to depth D (where 0 means no limit); for each generator, temporarily defines a procedure named K, and calls {yield}. {K A} returns A in the context of the current generator. {generators K} is equivalent to {generators K '' 1}. {generators K P} is equivalent to {generators K P 1}.

input-file-bytes Procedure

{input-file-bytes F} returns the contents of the file with path F (which must be relative to the input directory) decoded using Latin-1.

input-file-size Procedure

{input-file-size F} returns the size in bytes of the file with path F, which must be relative to the input directory.

input-file-string Procedure

{input-file-string F} returns the contents of the file with path F (which must be relative to the input directory) decoded using UTF-8.

input-file-timestamp Procedure

{input-file-timestamp F} returns the modification timestamp of the file with path F (which must be relative to the input directory) in the ISO-8601 year-month-day-hour-minute-second-timezone format. The resulting string reflects the time in UTC, but {input-file-timestamp F true} reflects the time according to the local time zone.

input-files Procedure

{input-files K P D} iterates the files in the path P (which must be relative to the input directory) limited to depth D (where 0 means no limit); for each file, temporarily defines a procedure named K that returns the current file name, and calls {yield}. {input-files K} is equivalent to {input-files K '' 1}. {input-files K P} is equivalent to {input-files K P 1}.

write Procedure

{write A} writes A to the file that is currently being generated.

5.11. Math

< Procedure

{<} and {< X} return true. {< X1 X2 ... Xn} is equivalent to {< X2 ... Xn} if X1 is numerically less than X2; otherwise, returns false.

<= Procedure

{<=} and {<= X} return true. {<= X1 X2 ... Xn} is equivalent to {<= X2 ... Xn} if X1 is numerically equal or less than X2; otherwise, returns false.

= Procedure

{=} and {= X} return true. {= X1 X2 ... Xn} is equivalent to {= X2 ... Xn} if X1 is numerically equal to X2; otherwise, returns false.

> Procedure

{>} and {> X} return true. {> X1 X2 ... Xn} is equivalent to {> X2 ... Xn} if X1 is numerically greater than X2; otherwise, returns false.

>= Procedure

{>=} and {>= X} return true. {>= X1 X2 ... Xn} is equivalent to {>= X2 ... Xn} if X1 is numerically equal or greater than X2; otherwise, returns false.

- Procedure

{- Z W} returns the subtraction of W from Z. {- W} is equivalent to {- 0 W}. {- Z W ...} is equivalent to {- Z {+ W ...}}.

/ Procedure

{/ Z W} returns the division of Z by W. {/ W} is equivalent to {/ 1 W}. {/ Z W1 W2 ... Wn} is equivalent to {/ {/ Z W1} W2 ... Wn}.

* Procedure

{*} returns 1. {* Z ...} returns the product of its arguments.

% Procedure

{% N M} returns N modulo M.

+ Procedure

{+} returns 0. {+ Z ...} returns the sum of its arguments.

abs Procedure

{abs X} returns the absolute value of X.

ceiling Procedure

{ceiling X} returns the smallest integer that is at least as large as X.

exact Procedure

{exact Z} returns the coercion of Z into an exact number.

floor Procedure

{floor X} returns the largest integer that is no more than X.

inexact Procedure

{inexact Z} returns the coercion of Z into an inexact number.

max Procedure

{max X ...} compares its arguments as numbers and returns the largest.

min Procedure

{min X ...} compares its arguments as numbers and returns the smallest.

round Procedure

{round X} returns the integer closest to X, resolving ties in favor of an even number.

truncate Procedure

{truncate X} returns the integer farthest from 0 that isn't farther from 0 than X.

5.12. Special

The procedures defined in dictionary files accept any number of arguments, which can be accessed through these special procedures.

local Procedure

{local N} returns the N-th argument that was provided to the procedure where it appears; signals an error if no such argument exists. {local N D} returns D if there is no N-th argument.

locals Procedure

{locals} returns the number of arguments that were provided to the procedure where it appears. {locals P} iterates the arguments; for each argument, temporarily defines a procedure named P, which returns the argument, and calls {yield}.

6. Customization

You can place Racket code in a file named custom.rkt in your project's directory to customize Pinaret. This file should usually start with:

#lang racket

(require pinaret/custom)

in order to access Pinaret's symbols. An already running instance of pinaret-repl or pinaret doesn't detect changes to custom.rkt; you'll need to restart the program for the changes to take effect.

6.1. Static Files

(ignore-file? (const #f)) Parameter

Procedure used to determine which static files from the input directory must be ignored when the file generation has already finished and the program has to decide whether to do it again; it's only effective when the --loop command line option is provided. The procedure receives the file path as its only argument, and must return #f if the file mustn't be ignored.

(omit-file? (const #f)) Parameter

Procedure used to determine which files must be omitted when copying static files from the input directory to the output directory. The procedure receives the file path as its only argument, and must return #f if the file mustn't be omitted.

6.2. Procedure Groups

The predefined Pinaret procedures have restricted access to the system resources, but the custom procedures written in Racket may not. It's your responsibility to ensure your custom procedures are safe, if safety is one of your requirements.

current-procedures Parameter

List of available procedure groups. Usually, you don't need to use this parameter directly. Procedure groups are immutable hash tables created with hasheq, with symbols as keys and procedures as values. By default, it contains the predefined procedures, which should be kept there.

define-procedure-group Syntax

(define-procedure-group G A) defines the G symbol to refer to a new procedure group, and the A symbol, used to add procedures to the group. Example:

(define-procedure-group
  custom-procedures
  define-custom-procedure)

A Racket procedure which is meant to be used as a Pinaret procedure should always return a string, and should treat its arguments as thunks that return strings; calling the thunks is equivalent to evaluating the Pinaret arguments, and usually you'll only want to call them once.

To add a procedure to a group you have to supply the name of the procedure, an optional documentation string, and a Racket procedure. Example:

(define-custom-procedure hello
  "{hello U} returns a greeting for U."
  (lambda (who)
    (format "hello ~a" (who))))

define-procedure-group/provide Syntax

(define-procedure-group/provide G A) is equivalent to (define-procedure-group G A) (provide G). This is mostly useful when you are writing a module for a procedure group in order to reuse it in different custom.rkt files.

use-procedure-group Syntax

(use-procedure-group G) adds the procedure group G to current-procedures. Example:

(use-procedure-group custom-procedures)

If the procedure group you want to use is on a module, you may omit the require and use the syntax (use-procedure-group M G), where M is the name of the module. For example, if you have a module called pinaret-custom/misc with a procedure group called misc-procedures:

(use-procedure-group
  pinaret-custom/misc misc-procedures)

6.3. Procedures

apply-procedure Syntax

(apply-procedure P A ... L) applies the procedure P using the content of the list of unevaluated arguments L, with As prepended.

call-procedure Syntax

(call-procedure P A ...) calls the procedure P with As as arguments, which must be unevaluated.

procedure-ref Syntax

(procedure-ref S) returns the Pinaret procedure corresponding to the symbol S, or signals an error if it isn't found. The returned procedure isn't meant to be called directly, otherwise the recursion limit wouldn't be enforced and the backtraces displayed on error would be incomplete.

procedure-ref/apply Syntax

(procedure-ref/apply S A ... L) applies the Pinaret procedure corresponding to the symbol S using the content of the list of unevaluated arguments L, with As prepended, or signals an error if it isn't found.

procedure-ref/call Syntax

(procedure-ref/call S A ...) calls the Pinaret procedure corresponding to the symbol S with As as arguments (which must be unevaluated), or signals an error if it isn't found.

procedure-ref/forgive Procedure

(procedure-ref/forgive S) returns the Pinaret procedure corresponding to the symbol S, or returns #f if it isn't found. The returned procedure isn't meant to be called directly, otherwise the recursion limit wouldn't be enforced and the backtraces displayed on error would be incomplete.

6.4. Errors

To signal a Pinaret error from a Racket procedure, you must raise an error/params exception.

error/params? Procedure

(error/params? E) returns #t if E is an error/params exception, otherwise returns #f.

error/params-args Procedure

(error/params-args E) returns the arguments of the error/params exception E.

error/params-params Procedure

(error/params-params E) returns the parameterization of the error/params exception E.

error/params-raiser Procedure

(error/params-raiser A ...) may receive zero or more arguments, and returns a procedure that receives a single argument, let's call it B; if B is an error/params exception, a new error/params exception is raised with B's parameterization and with As prepended to B's arguments; if B isn't an error/params exception, an error/params exception is raised with the current parameterization and As with B on its tail. error/params-raiser is typically used in conjunction with with-handlers.

raise-error/params Procedure

The raise-error/params procedure raises an error/params exception that includes the current parameterization and the list of its arguments. By convention, the arguments should be symbols followed by other values.

6.5. Sequences

call-yield Syntax

(call-yield T) temporarily sets current-yield to T and calls the yield Pinaret procedure.

(current-yield #f) Parameter

The current-yield parameter contains the element processing coroutine, which must be either a thunk that doesn't necessarily return a string, or #f, when no sequence is expected. You may store the value of (current-yield) to later pass it to call-yield. For example, this is a simplified version of the drop procedure:

(define-custom-procedure drop-1
  (lambda (sequence)
    (let ((yield (current-yield))
          (dropped? #f))
      (parameterize
          ((current-yield
            (lambda ()
              (if dropped?
                  (call-yield yield)
                  (set! dropped? #t)))))
        (sequence))
      "")))

6.6. Booleans

boolean->string Procedure

(boolean->string B) returns the false string if B is false, otherwise returns the true string.

(false-string "false") Parameter

String used as a convention to represent the Boolean false value.

string->boolean Procedure

(string->boolean S) if S is the true string, returns #t; if it's the false string, returns #f; otherwise signals an error.

(true-string "true") Parameter

String used as a convention to represent the Boolean true value.

6.7. Numbers

string->divisor Procedure

(string->divisor S) reads and returns a number from the string S; signals an error if it's not possible, or if the number is zero.

string->integer Procedure

(string->integer S) reads and returns a number from the string S; signals an error if it's not possible, or if the number isn't an integer.

string->natural Procedure

(string->natural S) reads and returns a number from the string S; signals an error if it's not possible, or if the number isn't a natural number.

string->number* Procedure

(string->number* S) reads and returns a number from the string S; signals an error if it's not possible.

6.8. Date and Time

date->iso8601-string Procedure

(date->iso8601-string D) returns the ISO-8601 year-month-day-hour-minute-second-timezone string representation of the date D.

6.9. Regular Expressions

pregexp* Procedure

(pregexp* S) returns (pregexp S); signals an error if S isn't a valid regular expression

make-match-procedure Procedure

(make-match-procedure M), where M is a value returned by regexp-match (or an element of a list returned by regexp-match*), returns a procedure that can be used as a Pinaret procedure; let's call it P. {P 0} returns the match substring. {P} is equivalent to {P 0}. {P N}, where N is greater than 0, returns the match group number N, or signals an error when N is out of range.

6.10. Arguments

call-extended Procedure

(call-extended T K P) temporarily extends the set of procedures with the procedure P named K, which will usually be an unevaluated argument (a thunk that returns a string), and calls the thunk T. (call-extended T K1 P1 K2 P2) is equivalent to (call-extended (lambda () (call-extended T K2 P2)) K1 P1); any number of keys/values may be provided.

call-extended* Procedure

(call-extended* T L) temporarily extends the set of procedures with procedures from the list L, and calls the thunk T. L must contain an even number of unevaluated arguments (otherwise signals an error); the elements in the odd positions are the keys, and those in the even positions are the values. call-extended* accepts a keyword argument #:call-values? (which is #t if omitted); if it's #f the values won't be evaluated, otherwise they will be evaluated.

extended Procedure

(extended G L) returns the procedure group G with added procedures from the list L, which must contain an even number of unevaluated arguments (otherwise signals an error); the elements in the odd positions are the keys, and those in the even positions are the values. extended accepts a keyword argument #:call-values? (which is #t if omitted); if it's #f the values won't be evaluated, otherwise they will be evaluated.

6.11. File Generation

(bytes?-key 'bytes?) Parameter

Symbol of the bytes? procedure.

(dict-suffix ".dict") Parameter

String used as the file extension of dictionary files.

(index-file-key 'index-file) Parameter

Symbol of the index-file procedure.

(input-dir "input") Parameter

String used as the path to the input directory.

(input-key 'input) Parameter

Symbol of the input procedure.

(library-dir "library") Parameter

String used as the path to the library directory.

(mode-key 'mode) Parameter

Symbol of the mode procedure.

(output-dir "output") Parameter

String used as the path to the output directory.

(output-key 'output) Parameter

Symbol of the output procedure.

(path-key 'path) Parameter

Symbol of the path procedure.

6.12. REPL

(repl-prompt "\n> ") Parameter

String used as a prompt for the REPL.

(repl-prompt* "| ") Parameter

String used as a prompt for the REPL when more input is expected in order to evaluate the previous line.

6.13. Parser

(application-end-char #\}) Parameter

Character used to mark the end of a procedure application.

(application-start-char #\{) Parameter

Character used to mark the start of a procedure application.

(comment-key '|#|) Parameter

Symbol used to determine which procedure definitions to ignore in dictionary files.

(escape-char #\\) Parameter

Character used to escape the following character inside procedure applications or to remove the special meaning of | at the beginning of lines in values of dictionary files, or to avoid string interpolation outside procedure applications.

(filter-char #\|) Parameter

Character used to mark the application of a filter inside a procedure application.

(interpolation-char #\$) Parameter

Character used to mark the beginning of a string interpolation.

(key-char #\|) Parameter

Character used to mark the start of a procedure definition in the beginning of a line in a dictionary file.

(quote-chars '(#\' #\" #\`)) Parameter

List of characters than can be used to avoid the special meaning of whitespace characters and | inside a procedure application.

6.14. Runtime

(current-recursion-limit 1024) Parameter

Positive integer used as a limit to prevent infinite loops when calling recursive procedures.

(local-key 'local) Parameter

Symbol of the special local procedure.

(locals-key 'locals) Parameter

Symbol of the special locals procedure.