7.0.0.6

2 Lab Syntax, More Syntax, ...

Goals

become familiar (re-acquainted) with define-syntax and syntax-parse

remember Racket functions and forms available for formulating “target” expressions

The following exercises request the development of a range of syntactic Racket extensions. Some of these already exist under a different name; don’t just compile the new surface syntax to those but develop your own translation. (If you think a feature is better off implemented as a run-time function, do so.)

Please make sure to solve the first and the last exercise; of the remaining, solve as many as you can. Remember to work in pairs.

Exercise 1. Modify define-world* so that it binds each given identifier to its place in the sequence. We start counting at 0 so that

(define-world* x y z)

binds x to 0, y to 1, and so on.

Consider two variants: one that compiles the values at compile time and another one that computes them at run time. The second one might be a bit easier, but do try your hands at both. image

Exercise 2. Develop the loop extension. It has the following grammar:
  Expression = ...
  | (loop Variable0 ((Variable1 Expression1) ...) Expression0 ...)

The extension maps the surface syntax to a recursive function, named Variable0, whose parameters are Variable1 ..., whose body is Expression0 ..., and immediately applies it to the arguments Expression1 .... The Variable0 function is not visible outside of the loop expression. image

Exercise 3. Develop the all extension. It has the following grammar:
  Expression = ...
  | (all Expression ...)
If all of the expressions evaluate to a non-#false value, all produces the list of the results; otherwise it produces #false. image

Exercise 4. Develop the dispatch extension. It has this grammar:
  Expression = ...
  | (dispatch Expression DClause ...)
  | (dispatch Expression DClause ... [orelse Expression])
     
  DClause = [(Identifier ...) Expression]

It evaluates the first Expression to a symbol; if it produces anything else, dispatch raises an error. Next dispatch checks whether the symbol occurs in any of the identifier lists (interpreted as list of symbols) and evaluates all corresponding expressions. If the symbol does not show up in any of the identifier lists and an orelse clause is present, the expression in that clause is evaluated.

Example
(define (f x)
  (dispatch x
            [(x y) (displayln 0)]
            [(z x) (displayln 1)]
            [orelse (displayln 2)]))
For (f 'x), x shows up in both regular clauses, while for (f 'a) a does not show up at all. image

Exercise 5. You might have gotten tired of writing
(define-syntax (f stx)
  (syntax-parse stx ...))
If so, you have what it takes to recognize when a language extension is called for. If not, you should solve some more of the exercises.

Develop define-rewrite-rule, which accepts a syntax pattern and a syntax template and then synthesizes an appropriate combination of define-syntax and syntax-parse.

Example

(define-rewrite-rule
  (loop-for-ever exp)
  ; >
  (local ((define (for-ever) (begin exp (for-ever)))) (for-ever)))
image