Skip to content

Instantly share code, notes, and snippets.

@creationix
Last active July 10, 2017 20:53
Show Gist options
  • Save creationix/ab9e99102d2a40c0f82c to your computer and use it in GitHub Desktop.
Save creationix/ab9e99102d2a40c0f82c to your computer and use it in GitHub Desktop.

Lisp syntax, but using whitespace to remove most parentheses.

  • Every line is wrapped as a list unless it's prefixed with an @ symbol.
  • Empty lines are ignored.
  • Indented lines are concatenated to their parent lists.
  • An inline arrow acts like indentation without wasting whitespace.
  • Use square brackets for inline lists.
  • Use round parentheses for infix notation (first two items are swapped)

For example, take the following lisp style program:

(print "Hello World")

(for (x 10)
  (for (y 10)
    (printf "%d + %d = %d" x y (+ x y))
  )
)

In my modified syntax, this would look like:

print "Hello World"

for [x 10] → for [y 10]
  printf "%d + %d = %d" x y (x + y)

A recursive Fib function could look like:

def fib → λ [n]
  if (n ≤ 2)
    @ 1
    @ ([fib (n - 1)] + [fib (n - 2)])
-- Standard lisp style syntax, but each line is assumed to be wrapped in parens
print "Hello World"
-- Loop 10 times with i changing from 0 to 9
-- Indent means to continue parent list, but each line is own sub-list
for [i 10]
print i
-- Loop over a list of names
for [name names]
print name
-- Nested loops using → syntax
-- → means to assume everything afterwards is indented
for [y 10] → for [x 10]
print x y
-- Lines starting with @ are not wrapped as lists
def fib → λ [n]
if (n ≤ 2) 1
@ ([fib (n - 1)] + [fib (n - 2)])
-- Object syntax
obj
@ name "Tim"
@ age 33
-- Object set, get
get person "name"
@ person.name
set person "name" "Tim"
-- Sample program: maze generator
-- Sample output for 3x3 maze
-- ██████████████
-- ██ ██ ██
-- ██ ██████ ██
-- ██ ██
-- ██ ██ ██████
-- ██ ██ ██
-- ██████████████
def width 30
def height 30
def size (width × height)
-- Cells define the maze
def cells → map [i size] → table
-- parent is initially null
@ parent nil
-- right and down walls are initially filled
@ right true
@ down true
-- Define the sequence of index and right/left
def ww (width - 1)
def hh (height - 1)
-- Create a list of actions to perform in shuffled order
def sequence → shuffle → concat
map [i size] → if ((i % width) < ww) → table
@ cell (cells get i)
@ direction "right"
map [i size] → if ((i ÷ width) < hh) → table
@ cell (cells get i)
@ direction "down"
-- Find the root of a set cell -> cell
def find-root → λ [cell] → if cell.parent
find-root cell.parent
@ cell
for [item sequence]
def direction item.direction
def root → find-root (cells . i)
def horizontal → ? (item . 0)
def other → find-root (cells . (i + [? (item . 0) 1 width)))
(if (≠ (. root 0) (. other 0))
(. root 1 other)
(. (. walls i) (? (. item 0) 0 1) false)
)
)
def w (width × 2)
def h (height × 2)
join "\n" → map [y (h + 1)]
join "" → map [x (w + 1)]
¿ " " "██" → or
-- Four outer edges are always true
= x 0
= y 0
= x w
= y h
-- Inner cells are more complicated
? (y % 2)
? (x % 2)
-- cell middle
false
-- cell right
(. (. walls (+ (÷ (- x 1) 2) (× (÷ y 2) width))) 0)
)
(? (% x 2)
-- cell down
(. (. walls (+ (÷ x 2) (× (÷ (- y 1) 2) width))) 1)
-- cell corner
true
)
)
))
))
))
@jeapostrophe
Copy link

Implement it in Racket and see how it works out for real programs. it is easy to make a new reader at the #lang level and just use the rest of Racket syntax. Make sure you read about the history of paren-less LISP though, such as sweet.

@creationix
Copy link
Author

@jeapostrophe you mean like? http://readable.sourceforge.net/

I've read that and it's where I got the idea of an infix syntax using a different bracket. I decided I prefer round parens for infix and square for regular lists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment