Here are the current rules for draft 0.2 of sweet-expressions, as shown in:
http://www.dwheeler.com/readable/version02.html
I'd love to hear feedback!
Sweet-expressions are simply extensions to traditional s-expressions, and are
implemented by adding capabilities to the reader (usually named "read"). A
sweet-expression reader can read ordinary s-expressions as usual, but can also
read other syntax that are handy abbreviations - just as 'a is a handy
abbreviation of (quote a).
Here are the draft sweet-expressions version 0.2 rules:
1. Prefixed (...). Syntax of the form e(...) — with no whitespace
between expression e and the open parenthesis — are mapped to (e ...).
Any parameters in "..." are space-separated. This produces another expression,
so this can be repeated (left-to-right). ‣ This adds support for
traditional function notation. For example, "cos(x)" maps to "(cos x)", "max(3
4)" maps to "(max 3 4)", and "f(x)(a b)" maps to "((f x) a b)". Note that this
is especially convenient for certain styles of functional programming,
including lambda expressions.
2. Unprefixed (...). An expression beginning with an unprefixed open
parenthesis "(" begins a traditional s-expression until its matching ")".
‣ Implementations would typically call a "standard" s-expression reader.
Because of this rule, a sweet-reader is extremely backward-compatible; a
sweet-reader can read nearly all existing Lisp files. Note that any "("
preceded by whitespace, "(", "{", or "[" is unprefixed.
3. Unprefixed {...}, aka infix. An unprefixed {...} contains an "infix
list". If the enclosed infix list (1) has an odd number of parameters, (2) has
at least 3 parameters, and (3) has equal even parameters, then it is mapped to
"(even-parameter odd-parameters)". Otherwise, it is mapped to "(nfx list)"
— you'll need to have a macro named "nfx" to use it. ‣ This rule
means that {n = 0} maps to (= n 0), {3 + 4 + 5} maps to (+ 3 4 5), {3 + {4 *
5}} maps to (+ 3 (* 4 5)), and {3 + 4 * 5} maps to (nfx 3 + 4 * 5).
Consistently using {...} so infix operators are never mixed in a list has the
advantage that all macros will see the usual list form with the function in the
first position. If you want operator precedence, define an nfx macro to
implement the precedence rules you desire. Or, if you never want precedence,
define nfx to be an error. Every infix operator must be surrounded by
whitespace for this rule to work as designed.
4. Prefixed {...}. A prefixed expression f{...}, where f is an expression,
is an abbreviation for f({...}). ‣ This rule simplifies combining
function calls and infix expressions when there is only one parameter to the
function call. Thus, f{n - 1} is equivalent to f({n - 1}), which maps to (f (-
n 1)). When there is more than one function parameter, use the normal
term-prefixing format, e.g., f({x - 1} {y - 1}) maps to (f (- x 1) (- y 1)).
5. Indentation. Indentation is meaningful; the "I-expressions" of Scheme
SFRI-49 are supported. An indented line is a parameter of its parent, later
terms on a line are parameters of the first term, and lists of lists are marked
with "group". A line with exactly one term, and no children, is simply that
item; otherwise that line and its child lines are themselves a new list.
Indentation is disabled inside the grouping pairs (), [], and {}, whether they
are prefixed or not. Blank lines at the beginning of a new expression are
ignored. ‣ A function call with 0 parameters must be surrounded or
immediately followed by a pair of parentheses: (pi) or pi().
6. Immediate completion. A single complete expression that begins at the
left edge of a line, and is immediately followed by newline after it completes,
causes the expression to immediately complete (without waiting for the next
line of input). ‣ Thus, entering load("hello") and immediately pressing
return will execute immediately, but load "hello" will require pressing Enter
twice (because there might be another line to follow). This rule makes
interactive use more pleasant, without harming file reading. It avoids two
problems: (1) having to press Enter twice to execute simple one-line commands,
and (2) allowing the command line to easily go "out of sync" (when, after
pressing return, the user sees the result of the previous line). Type an
initial space if you want to enter only a single complete expression on a line
yet follow it with child lines.
7. Unprefixed [...]. Unprefixed square brackets [..] disable indentation
processing inside them. ‣ Use [...], instead of (...), to get the benefit
of a list without disabling sweet-expression capabilities. These aren't used
often, but they are critically necessary when you need them. The contents are
not interpreted as infix, so [a + b(x)] maps to (a + (b x)).
8. Prefixed [...]. Prefixed square brackets e[...] maps to (bracketaccess e
...). ‣ Thus, "t[x]" maps to "(bracketaccess t x)". This is intended to
simplify use of indexed arrays, associative arrays, and similar constructs.
Note that usual Lisp quoting rules still work, so 'a still maps to (quote a).
But they work with the new capabilities, so 'f(x) maps to (quote (f x)). Same
with quasiquoting and comma-lifting. A ";" still begins a comment that
continues to the end of a line, and "#" still begins special processing.
Note that you have to disable indentation to use infix operators as infix
operators. This doesn't seem to be a problem in practice.
Shockingly, I've switched to infix as NOT the default, because in
experimentation I found that it didn't have the positive effects I expected.
Simply adding a way to mark infix lists using {...}, and a prefix f{...},
manages to do quite a lot. See my examples for more.
--- David A. Wheeler