SRFI-110 "Curly Infix,Neoteric and Sweet expressions" for Racket
Scheme Request For Implementations 110
Install as a package.
Use:
#lang reader SRFI-110
or:
| #reader SRFI-110 | package: SRFI-110-for-Racket |
Designed to be used with Scheme+:
| (require Scheme+) | package: Scheme-PLUS-for-Racket |
Sweet-expressions (indentation) work in files: the reader reads a file as sweet-expressions, and code written with parenthesis gives the same forms. Scheme+ can be written with this syntax (see examples/sweet-expressions+.rkt):
#lang reader SRFI-110 module m racket require Scheme+ define (f x) {x + 1} display f(2)
gives (module m racket (require Scheme+) (define (f x) (+ x 1)) (display (f 2))). With the pragma BEGIN-STRICT-SRFI-105-REGION the curly brackets follow SRFI 105 ({a * b + c} is ($nfx$ a * b + c)): the test suite of sweet-expressions of the Readable project then passes as with the original reader (see Strict mode: using the reader as the original SRFI 110).
An empty line ends an expression: inside a block, separate the lines with comment lines. The blocks of the if of Scheme+ can be indented under then and else (they are read as (then …) and (else …), which the if accepts); the keyword that ends a loop (while of do, until of repeat) is written alone on its line, at the level of the expressions. ! at the beginning of a line is an indentation character (write {n !} or (! n) for the factorial). The REPL stays in curly infix: in sweet-expressions an input ends only with an empty line.
A file is read as sweet-expressions. As specified by SRFI 110, a parsing directive written alone at the beginning of a line (but not as the very first characters of the file) changes the reading of the file from the next datum: #!curly-infix reads the rest of the file with the rules of SRFI 105, which accept all the standard Scheme, even two forms glued without a space such as (display x)(newline) (in sweet-expressions, as in SRFI 110, it is the call ((display x) newline)); #!sweet goes back to the sweet-expressions; #!no-sweet reads with the read of Racket. For example, a file written in standard Scheme with curly brackets can begin with:
#reader SRFI-110 |
#!curly-infix |
1 Pragmas
A pragma is a bare symbol written in the source code: it is recognized by the reader while it reads the file, not by the Racket compiler, and it does not remain in the code.
Pragmas |
| Effect |
| At the beginning of each file |
| Region |
BEGIN-STRICT-SRFI-105-REGION |
| the curly brackets follow SRFI 105 |
| rules of Scheme+ |
| from the pragma to the other one |
ANNOTATION-ON |
| type annotation and inference pass of Scheme+ |
| off |
| the elements that follow the pragma in the same list |
CARE-OF-QUOTES-ON |
| the quoted lists are data, |
| on (data) |
| as ANNOTATION-ON |
A pragma is written alone, or alone in curly brackets ({BEGIN-STRICT-SRFI-105-REGION}); in sweet-expressions it is written alone on its line. It can be written inside a list (the body of the module form, of a procedure, ...) or at the top level of the file, before the module form, where it acts on the whole module. A pragma acts only on the file where it is written, not on the modules this file requires: each file starts in the state of the table.
1.1 Strict mode: using the reader as the original SRFI 110
Between BEGIN-STRICT-SRFI-105-REGION and END-STRICT-SRFI-105-REGION the reader follows SRFI 110, as the original reader. The curly brackets follow SRFI 105: {a} is a, {a b} is (a b), {a op b op c} with a single operator is (op a b c) (also with if), the () are not infix, and the other expressions are ($nfx$ …). The neoteric expressions (f(x), f{x}, x[i]) and the sweet-expressions work as in SRFI 110; unlike SRFI 105, the neoteric expressions are recognized everywhere, in both modes. The test suite of the sweet-expressions of the Readable project then passes as with the original reader (examples/sweet-testsuite/run-sweet-testsuite.rkt).
The main differences with the rules of Scheme+:
Expression |
| Scheme+ |
| Strict mode |
{a + b * c} |
| (+ a (* b c)) |
| ($nfx$ a + b * c) |
'{a + b * c} |
| '(+ a (* b c)) |
| '($nfx$ a + b * c) |
{n !} |
| (! n) |
| (n !) |
{a if b} |
| (if b a) |
| (if a b) |
{f x} |
| decided at run time |
| (f x) |
{a + b + c} , {- x} , {x} |
| (+ a b c) , (- x) , x |
| the same |
Racket defines neither $nfx$ (used for the expressions that are not simple infix) nor $bracket-apply$ (used for x[i]): SRFI 105 leaves them to the programmer. Define them yourself, or take them from Scheme+, where $nfx$ evaluates the expression with the precedence of the operators. A whole file in strict mode, written with sweet-expressions, as with the original reader:
#lang reader SRFI-110 BEGIN-STRICT-SRFI-105-REGION module m racket require (only-in Scheme+ $nfx$ $bracket-apply$) define f(x) {x * 2} define v #(10 20 30) display $ list {2 + 3 * 4} {f(5) + v[1] * 2} '{a + b * c} newline()
displays (14 50 ($nfx$ a + b * c)).
At the REPL, which stays in curly infix, type the pragma alone (or alone in curly brackets): the mode changes for the rest of the session.
1.2 Type annotation: ANNOTATION-ON and ANNOTATION-OFF
The pragmas ANNOTATION-ON and ANNOTATION-OFF turn on or off, by region, the type annotation and inference pass of Scheme+ (see the section Type annotation and inference of the Scheme+ documentation): a pragma acts on the elements that follow it in the same list and on all that is nested in them, until the other pragma or the end of the list. The pass is off at the beginning of each file: a pragma acts only on the file where it is written, not on the modules this file requires. It can be written inside the module form or at the top level of the file (before the module form, it acts on the whole module).
At the REPL, type the pragma alone (followed by an empty line, as any input): it turns the annotation on or off for the rest of the session, the expressions typed between ANNOTATION-ON and ANNOTATION-OFF are a region. The REPL starts in the state left by the file it was started from, and ANNOTATION-ON also works in a REPL started without a file: it then creates the tables of the annotation, the tables of a file already read are kept.
1.3 Quoted lists: CARE-OF-QUOTES-ON and CARE-OF-QUOTES-OFF
The pragmas CARE-OF-QUOTES-ON and CARE-OF-QUOTES-OFF act by region too, on the quoted lists (see the section Pragmas of the Scheme+ documentation): with CARE-OF-QUOTES-ON, the state at the beginning of each file, a quoted list is data and a quoted {} is converted at its top level only; CARE-OF-QUOTES-OFF opens a region for the symbolic computation, where the quoted and quasiquoted lists are infix expressions at all levels ('(x + y * z) is '(+ x (* y z))).
2 Converting a Scheme+ file into a Scheme file
The command line tool src/curly-infix2prefix4racket.rkt of this package converts a Scheme+ file into the Racket code that the reader gives to the compiler, for example to debug it with the Racket tools (error line numbers, macro stepper, ...):
racket curly-infix2prefix4racket.rkt [--verbose] file+.rkt > file.rkt |
The converted code is written on the standard output. When the Scheme+ file has the pragma ANNOTATION-ON, it is the annotated code, the same as the code compiled by the reader; it never contains the type forms nor the pragmas. With the option –verbose, the banner and the state of the type annotation are displayed on the error output.