项目作者: tonyg

项目描述 :
Indentation-based Racket Syntax
高级语言: Racket
项目地址: git://github.com/tonyg/racket-something.git
创建时间: 2016-07-06T21:36:19Z
项目社区:https://github.com/tonyg/racket-something

开源协议:Other

下载


Indentation-based Racket Syntax with Macros and Infix Operators

Not sweet-exps (see Asumu’s
racket implementation).
Not srfi-49. More
inspired by Python and Haskell.

(The name “something” is temporary, and likely to be repurposed.)

Installation

Check out the repository. Then, in the directory containing
Makefile,

  1. make link

or

  1. raco pkg install --link -n something `pwd`/src

The main idea

S-expressions, but with usually-implicit parentheses. Indentation for
grouping is explicitly represented in the S-expression returned from
the reader.

This program:

  1. #lang something
  2. for { x: 1 .. 10 }
  3. def y: x + 1
  4. printf "x ~a y ~a\n" x y

… reads as this S-expression:

  1. (module something-module something/base
  2. (#%rewrite-body
  3. (for (block (x (block (1 .. 10))))
  4. (block (def y (block (x + 1)))
  5. (printf "x ~a y ~a\n" x y)))))

The #%rewrite-body macro, together with its companion
#%rewrite-infix, consults an operator table, extendable via the
def-operator macro, to rewrite infix syntax into standard prefix
S-expressions.

The block syntax has many different interpretations. It has a macro
binding that turns it into a Racket match-lambda*, and it is used as
literal syntax as input to other macro definitions.

For example, here’s one possible implementation of that for syntax:

  1. #lang something
  2. provide
  3. for
  4. require
  5. for-syntax something/base
  6. prefix-in base_ racket/base
  7. def-syntax for stx
  8. syntax-case stx (block)
  9. _ (block (v (block exp)) ...) (block body ...)
  10. (syntax (base_for ((v exp) ...) body ...))
  11. def-operator .. 10 nonassoc in-range

Notice how the block S-expressions are rewritten into a normal
S-expression compatible with the underlying for from racket/base.

Generally, all of these forms are equivalent

  1. x y z x y z: x y z { a; b }
  2. a a
  3. b b

and they are read as

  1. (x y z (block a b))

and are then made available to the normal macro-expansion process
(which involves a new infix-rewriting semi-phase).

Colons are optional to indicate a following suite at the end of an
indentation-sensitive line. Indentation-sensitivity is disabled inside
parentheses. If inside a parenthesised expression,
indentation-sensitivity can be reenabled with a colon at the end of a
line:

  1. a b (c d:
  2. e
  3. f)
  4. = (a b (c d (block e f)))
  5. a b (c d
  6. e
  7. f)
  8. = (a b (c d e f))

Conversely, long lines may be split up and logically continued over
subsequent physical lines with a trailing \:

  1. a b c \
  2. d \
  3. e
  4. = (a b c d e)

Semicolons may also appear in vertically-laid-out suites; these two
are equivalent:

  1. x y z
  2. a
  3. b; c
  4. d
  5. x y z { a; b; c; d }

Suites may begin on the same line as their colon. Any indented
subsequent lines become children of the portion after the colon,
rather than the portion before.

This example:

  1. x y z: a b
  2. c d
  3. e

reads as

  1. (x y z (block (a b (block (c d) e))))

Square brackets are syntactic sugar for a #%seq macro:

  1. [a; b; c; d e f] (#%seq a b c (d e f))
  2. [ (#%seq a (b (block c)) (d e f))
  3. a
  4. b
  5. c
  6. d e f
  7. ]

Forms starting with block in expression context expand into
match-lambda* like this:

  1. {
  2. pat1a pat1b
  3. exp1a
  4. exp1b
  5. pat2a
  6. exp2
  7. }
  8. (match-lambda*
  9. [(list pat1a pat1b) exp1a exp1b]
  10. [(list pat2a) exp2])

The map* function exported from something/lang/implicit differs
from map in racket/base in that it takes its arguments in the
opposite order, permitting maps to be written

  1. map* [1; 2; 3; 4]
  2. item:
  3. item + 1
  4. map* [1; 2; 3; 4]
  5. item: item + 1
  6. map* [1; 2; 3; 4]: item: item + 1
  7. map* [1; 2; 3; 4] { item: item + 1 }

A nice consequence of all of the above is that curried functions have
an interesting appearance:

  1. def curried x:: y:: z:
  2. [x; y; z]
  3. require rackunit
  4. check-equal? (((curried 1) 2) 3) [1; 2; 3]

A larger example

More examples can be found in the examples and
src/something/test directories.

  1. #lang something
  2. require
  3. racket/pretty
  4. something/infix
  5. for-syntax something/lang/implicit
  6. except-in xml document
  7. def-syntax single-xexpr stx
  8. syntax-case stx (= block)
  9. _ str
  10. string? (syntax-e . (syntax str))
  11. syntax str
  12. _ (= expr)
  13. syntax expr
  14. _ (tag (attr attr-expr) ... (block xexpr ...))
  15. syntax (list (quote tag) (list (list (quote attr) attr-expr) ...) (single-xexpr xexpr) ...)
  16. _ (tag (attr attr-expr) ...)
  17. syntax (list (quote tag) (list (list (quote attr) attr-expr) ...))
  18. def-syntax xexpr stx
  19. syntax-case stx (block)
  20. _ (block xexpr)
  21. syntax (single-xexpr xexpr)
  22. def-operator ++ 50 left string-append
  23. def-operator = 10 prefix =
  24. def document
  25. xexpr
  26. html
  27. head
  28. meta (http-equiv "Content-Type") (content "text/html; charset=utf-8")
  29. title: "Test page"
  30. body
  31. h1: "Hello"
  32. p: "Hello, world"
  33. h2: "Testing"
  34. p
  35. = "Hello, " ++ number->string (3 + 4)
  36. "! This rules."
  37. p
  38. "Another way of putting it would be to say that 3 + 4 = "
  39. = (number->string (3 + 4))
  40. "."
  41. pretty-print document
  42. printf "\n~a\n" (xexpr->string document)

A note on lexical syntax

The lexical syntax of this reader is not exactly that of Racket. For
example, comments start with // rather than ;, and the set of
allowable non-escaped identifiers is different (smaller).

I will likely revise this decision to bring it to be much closer to
Racket’s lexical syntax.

Emacs mode

See sth8.el.

Licence

Copyright (C) 2016–2019 Tony Garnock-Jones tonyg@leastfixedpoint.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this program (see the files “lgpl.txt” and
“gpl.txt”). If not, see http://www.gnu.org/licenses.