[racket-dev] Possible P4P or Honu ideas

From: Everett (webj2 at unoc.net)
Date: Fri Aug 13 16:08:08 EDT 2010

Does anyone think this idea would work for P4P or Honu?

#lang alt-syntax

Sets up a new reader that directly transforms the input into normal
S-Exprs, making this fully compatible with existing Racket (#lang
altsyntax would provide all of racket) and with macros.

Lexer Tokens
  ( ) [ ] { } (| |) _( : ): id string number char keyword etc...

    : id
    | constant

    //Application with expr in app position
    | '{' expr '}' '(' expr_lst ... ')' -> '(' expr expr_lst ... ')'
    //Normal application f(a ...)
    | id '(' expr ... ')' -> '(' id expr ... ')'
    //Infix application
    | '(|' expr_0 expr_1 expr_2 '|)' -> '(' expr_1 expr_0 expr_2 ')'

    //Normal prefix s-exprs
    | '[' expr ... ']' -> '(' expr ... ')'
    | '_(' expr ... ')' -> '(' expr ... ')'

    //Rules that use colon to replace parens
    | id {no_ws()}? ':' ( { checkIndentation() }? expr ) ... -> '('
expr ... ')'
    | ':' ( { checkIndentation() }? expr ) ... -> '(' expr ... ')'
    | id '(' expr_0 ... '):' ( { checkIndentation() }? expr_1 ) ... ->
'(' id expr_0 ... expr_1 ...')'

Indentation rule for ':', checked by "checkIndentation()", is:
- Remember the column of the colon and indentation of the line it is on.
- exprs on the same line are included
- if there are no exprs on the same line, then include lines of the same
indentation for which that indentation is greater than the indentation
on the colon's line.
- if there is something on the same line as the colon, then include
lines of the same indentation for which that indentation is greater than
the colon's column.

The "no_ws()" check checks column of colon vs end column of id to make
sure there's no WS between them.  This could be done by a lexer
returning ID and ID_COLON tokens instead.

Note that the "{ code-that-returns-boolean }?" notation is a "semantic
predicate" in ANTLR. Also "->" is a tree rewrite in ANTLR too.

- Use Shriram's idea of ':' to indicate structure (like if, let, etc.)
- Use Shriram's idea of {} around expr in application position
- Keep LL(1)
- Use indentation to replace parens when using ':'
- Make this a direct and obvious conversion to S-Expr to maintain
library and macro compatability (not just for students!)
- Allow an infix notation, which is very helpful for math and
comparison.  I use "(|" "|)" to maintain LL(1) parsing without stealing
{} from Shriram's use or '<' '>' which eliminates those chars in
operators, etc.
- Have function calls look like normal math, like f(a)
- Using [] is akin to saying "tuple", which is how it's often used in
let, for, etc. to pair id and value. So we can just parse them as normal
- Provide a way to write normal s-exprs if desired.  Making everything
use [] could work, but I gave "_(" ")" as an alternative to allow
varying things.
- Code should be visually structured very much like Racket is now.

Below shows some examples.

let: loop :[acc 1]
           [acc2 2]
     set!(a 1)
     set!(b (|acc2 + 1|))
     loop((|acc + a|) b)
{lambda: [a] a}(1)

[f a]
_(f a)

{g}(a b)
g(a b)
[g a b]
_(g a b)
(|a g b|)

if: (|a > 0|)

;if no tokens on the rest of the line, indent > line_indent vs. indent >
  "Hello World"]

match: thing
       ["a" display("Thing A")]
       ["b" display("Thing B")]
       [else display("Unknown")]

  ["a" display("Thing A")]
  ["b" display("Thing B")]
  [else display("Unknown")]

;Blake's idea.  Allow params both in parens and after colon for better
structure formatting.
match (thing):
  ["a" display("Thing A")]
  ["b" display("Thing B")]
  [else display("Unknown")]

if((|a > 0|)):

;Can be abused too, but you'd never do that, right?
match (thing 
       ["a" display("Thing A")]):
  ["b" display("Thing B")]
  [else display("Unknown")]

let: :[a 1]
      [b 2]
      [c 3]
    _(+ a b c)

let:   :[a 1][b 2][c 3]
     [+ a b c]

  :[a 1][b 2]
   [c 3]
  +(a b c)
  test-term-equal(lhs rhs)
  test-equal(term(lhs) term(rhs))

[define-syntax-rule [test-term-equal lhs rhs]
  [test-equal [term lhs] [term rhs]]]


;Use of infix for non-math operations works too, of course.
(| range(1 100) map + |)

;The following is a Redex grammar (a piece of a grammar I'm using)
  e: v
     setop([pattern in e] e)
     if(e e e)
     let( [[id e]] e)
     op(e ...)
  k: ret
     setop(pattern e k)
     if(e e k)
     pop(eta k)
     let(id e k)
     op( [v ...] [e ...] k)
  v: number
     true false
     [addr address]
     [const-set v ...]
     [const-tuple v ...]

-Everett Morse

Posted on the dev mailing list.