Lispex Syntax: The Atoms of Thought

There are no complex rules. Explore the pure world of tokens, literals, and S-expressions—the smallest units that build all of Lispex's logic.

Welcome to the Lispex syntax reference. This page defines the input‑only S‑expression grammar (R7RS‑small inspired) and the deterministic normalization rules that rewrite source into the Core AST. Execution semantics and standard library behavior are out of scope.

Scope: input grammar + normalization only. No user macros or reader extensions are allowed. A compatibility toggle R5RS-compat adjusts a few details (§12 in the full spec).

0) File Header (optional)

You may declare version/toggles at the top of a file as line comments:

;! lispex 1.1
;! compat: r5rs   ; enable R5RS-compat rules

1) Encoding / Whitespace / Comments

  • Encoding: UTF‑8
  • Whitespace: space, tab, CR, LF
  • Comments:
    • line: ; ...\n
    • block: #| ... |# (nested)

2) Tokens & Literals

Symbols (identifiers)

Case‑sensitive symbols (Unicode allowed). Conceptually: [^()\[\]{}\s"';,]+` (informal). Reserved words cannot be bound (see Reserved).

Booleans

#t   #f

Numbers (input forms)

  • Integers (decimal): 0 | -?[1-9][0-9]*
  • Rationals: p/q (q ≠ 0)
  • Reals: -?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?

Precision/accuracy is outside this page’s scope (input surface only).

Characters / Strings

  • Characters: #\a, #\space, #\x03B1
  • Strings: "..." with escapes \" \\ \n \t \r \xHH;

Lists / Dotted Lists / Vectors / Bytevectors

  • Lists: (a b c); dotted: (a b . c)
  • Vectors: #(a b c)
  • Bytevectors: #u8(0 255 ...)

Quote family (reader shorthands)

'x      ⇒ (quote x)
`x      ⇒ (quasiquote x)
,x      ⇒ (unquote x)
,@xs    ⇒ (unquote-splicing xs)

3) Module Header (optional)

(module <name>
  (export id ...)
  (import <name> ...)
  body ...)

<name> is a symbol or dotted path (util.string). No rename/only/except in this surface header. Files without a module header are allowed.

Note: This header is optional and not used in the Guides. Lispex is a transformer; guides favor plain top‑level forms to keep examples minimal. Tooling may flatten or ignore this header during transformation.

4) Reserved (keywords) — cannot be bound

quote quasiquote unquote unquote-splicing
lambda if begin set! define let let* letrec
cond case and or when unless do
values call-with-values
call/cc dynamic-wind
module export import

Immediate error if user macro machinery is used: define-syntax syntax-rules syntax-case let-syntax letrec-syntax #lang #; (user macros/reader extensions are not part of the input grammar).

5) Core Forms — remain after normalization

(lambda (formals) body+)
(if test then else)
(begin e1 ... en)
(set! id expr)
(define id expr)
(define (f params) body+)      ; sugar → define+lambda during normalization
(let ((id expr) ...) body+)
(letrec ((id expr) ...) body+)
(quote datum)
(quasiquote template)          ; expands per §10 rules to Core forms
(values expr*)
(call-with-values producer consumer)
(call/cc proc)
(dynamic-wind before thunk after)

6) Derived Forms — deterministic normalization

Function definition sugar

(define (f a b) body...) ⇒ (define f (lambda (a b) body...))

let*

(let* ((x e1) (y e2) ...) body)
⇒ (let ((x e1)) (let* ((y e2) ...) body))

cond

(cond (t1 b1...) ... (else e...))
⇒ (if t1 (begin b1...)
      (if t2 (begin b2...)
          ...
          (begin e...)))

(=> variant is not in v1.)

case (uses equal? in the surface description)

(case key
  ((k11 k12 ...) b1...)
  ...
  (else e...))
⇒ (let ((t key))
     (if (equal? t k11) (begin b1...)
         (if (equal? t k12) (begin b1...)
             ...
             (begin e...))))

and / or

(and)              ⇒ #t
(and e1)           ⇒ e1
(and e1 e2 ... en) ⇒ (if e1 (and e2 ... en) #f)

(or)               ⇒ #f
(or e1)            ⇒ e1
(or e1 e2 ... en)  ⇒ (let ((t e1)) (if t t (or e2 ... en)))

R5RS‑compat notes: when ;! compat: r5rs is enabled, bytevector syntax and a few predicate/port details follow R5RS variants where applicable. See the full spec for the exact list.