Skip to content

Instantly share code, notes, and snippets.

@wilbowma
Created April 5, 2022 22:47
Show Gist options
  • Select an option

  • Save wilbowma/b177234545e2532d596828922a1dc8f8 to your computer and use it in GitHub Desktop.

Select an option

Save wilbowma/b177234545e2532d596828922a1dc8f8 to your computer and use it in GitHub Desktop.
Macros lecture live code
#lang racket
;; ------------------------------------------------------------------------
;; Template helpers
;; ------------------------------------------------------------------------
(define-syntax (.... stx)
(syntax-case stx ()
[_
#`(error "Incomplete template at line" #,(syntax-line stx))]))
(begin-for-syntax
(require (for-syntax racket))
(define-syntax (.... stx)
(syntax-case stx ()
[_
#`#'(error "Incomplete template at line" #,(syntax-line stx))])))
;; ------------------------------------------------------------------------
;; Syntactic sugar
;; ------------------------------------------------------------------------
#|
What is syntactic sugar?
|#
;; Proposed definitions:
;; 1. Nice ways to do something you can already.
;; 2. A transformation from one syntactic representation to another.
;; My definition: A mere extension of a language, expressed in terms of existing abstractions.
;; Bool-lang:
;; e ::= #t | #f | (if e e e) (let ([x e]) e)
;; Extension:
;; (or) -> #t
;; (or e) -> e
;; (or e es ...) -> (if e e (or es ...))
;; (or e es ...) -> (let ([x e]) (if x x (or es ...)))
;; EXERCISE 1:
;; Design and implement a compiler that compiles Bool-lang extended with `or` to
;; Bool-lang
(require racket/trace)
(trace-define (expand-bool-lang e)
(match e
[(? symbol?)
e]
[(? boolean?)
e]
[`(if ,es ...)
(cons 'if (map expand-bool-lang es))]
[`(let ([,x ,e1]) ,e2)
`(let ([,x ,(expand-bool-lang e1)])
,(expand-bool-lang e2))]
[`(or ,es ...)
(match e
[`(or) #t]
[`(or ,e) e]
[`(or ,e ,es ...)
(define x 'tmp)
(expand-bool-lang
`(let ([,x ,e])
(if ,x
,x
(or ,@es))))])]
#;[`(and ,es ...)
....]))
;; ------------------------------------------------------------------------
;; Macros
;; ------------------------------------------------------------------------
#|
What is a macro??
|#
;; Proposed definitions:
;; 1. User-definable syntactic sugar.
;; A macro is ...
;; A procedure that transforms "code"
;; A procedure that outputs ... the language.
;; A procedure that transforms syntax.
;; i.e. it takes in syntax and outputs syntax.
;; EXERCISE 2:
;; Design and implement a macro that compiles any `or` *expression* to a Bool-lang
;; *expression*
;; sexpr ?? -> ?? bool-lang
;; 1. What is the signature for this?:
; `(or ,es ...) ->
;; 2. How does we replace what was a recursive call to expand-bool-lang?
(define (or-macro e)
(match e
[`(or) #t]
[`(or ,e) e]
[`(or ,e ,es ...)
(define x 'tmp)
#;(or-macro
`(let ([,x ,e])
(if ,x
,x
(or ,@es))))
`(let ([,x ,e])
(if ,x
,x
(or ,@es)))]))
(require (for-syntax racket racket/trace))
#|
(dict-set! 'or '(lambda (stx) ....))
|#
(define-syntax or
(lambda (stx)
(define (or-macro e)
(match e
[`(or) #t]
[`(or ,e) e]
[`(or ,e ,es ...)
(define x 'tmp)
#;(or-macro
`(let ([,x ,e])
(if ,x
,x
(or ,@es))))
`(let ([,x ,e])
(if ,x
,x
(or ,@es)))]))
(datum->syntax stx (or-macro (syntax->datum stx)))))
(define-syntax and
(lambda (stx)
(define (and-macro e)
(match e
[`(and ,e1 ,e2)
`(if ,e1
,e2
#f)]))
(datum->syntax stx (and-macro (syntax->datum stx)))))
(module+ test
(require rackunit)
(check-equal? (or 5) 5)
(check-equal? (or) #t)
(check-equal? (or #f 6) 6)
(check-equal? (and (or #f #t) #t)))
;; ------------------------------------------------------
;; Exogenous macro expansion
;; ------------------------------------------------------
;; Let's create a design for Bool-lang that enables separating the macro
;; definition from the macro expansion.
;; EXERCISE 3:
;; Design and implement a macro expander for Bool-lang.
;;
;; Macros should be defined in a dictionary mapping their name or symbol to some
;; definition that can be interpreted by the expander.
(define (expand-bool-lang-ex stx [macros-env '()])
(define (expand stx)
(match stx
[`(,macro ,args ...)
#:when (dict-has-key? macros-env macro)
(expand ((dict-ref macros-env macro) stx))]
[`(,stuff ...)
(map expand stuff)]
[_ stx]))
(expand stx))
(expand-bool-lang-ex
'(or)
`((or . ,or-macro)))
;; ------------------------------------------------------
;; Endogenous macro expansion
;; ------------------------------------------------------
;; Let's create a design for Bool-lang that enables macro
;; definition inside the object language.
;; EXERCISE 4:
;; Design and implement an extension to Bool-lang that enables macros to be
;; defined inside the Bool-lang itself, and then used by the macro expander
;; after their definition.
;;
;; In principle, you should be able to reuse expand-bool-lang-ex, although you
;; may choose not to.
;; Bool-lang:
;; e ::= #t | #f | (if e e e) (let ([x e]) e)
;; (let-syntax ([x (lambda (stx) ...)]) e)
(define (expand-bool-lang-en stx)
(define (expand stx macros-env)
(match stx
[`(let-syntax ([,x (lambda (,stx) ,body)])
,e)
(expand e (dict-set macros-env x
(eval `(lambda (,stx) ,body)
(module->namespace 'racket))))]
[`(,macro ,args ...)
#:when (dict-has-key? macros-env macro)
(expand ((dict-ref macros-env macro) stx) macros-env)]
[`(,stuff ...)
(map (curryr expand macros-env) stuff)]
[_ stx]))
(expand stx '()))
(expand-bool-lang-en
'(let-syntax ([or (lambda (stx) (match stx [`(or) #t]))])
(or)))
;; Doesn't work; this expand doesn't allow shadowing
#;(expand-bool-lang-en
'(let-syntax ([or (lambda (stx) (match stx [`(or) #t]))])
(let ([or 5]) or)))
(let-syntax ([or (lambda (stx)
(syntax-case stx ()
[(_ e)
#'e]))])
(let ([or 6])
or))
;; macro generating macros are possible with endogenous macro expanders
#;(expand-bool-lang-en
'(let-syntax ([or (lambda (stx)
(match stx [`(or)
'(let-syntax ([and ()])
...
)]))])
(or)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment